@valbuild/shared 0.136.10 → 0.138.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,84 @@
1
1
  # @valbuild/shared
2
2
 
3
+ ## 0.138.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#754](https://github.com/valbuild/val/pull/754) [`6216172`](https://github.com/valbuild/val/commit/62161729aa40f737590b0a8093c0394385fffd8d) Thanks [@freekh](https://github.com/freekh)! - Connected projects hosted on the Val platform publish as a queued job too.
8
+
9
+ For a project with a GitHub repository whose site runs on the Val platform, pressing **Publish** is now a request, just as it is for a managed project. The Studio sends the job's changes; the content service pushes them as one commit on top of whatever is on the branch at that moment, so a developer's code push in between is kept. CI then builds it. A developer's change to the site's content files that this deployment has not seen stops the publish before anything is pushed, and says so.
10
+
11
+ - A build that CI reports as failed shows "Published, not on the site yet", with **View run**. The next publish builds again.
12
+ - New CLI command, `val ci-report --status failed|succeeded`, for a workflow's last step. It reads the commit, the branch and the run's address from GitHub Actions, uses the same `VAL_PROJECT_TOKEN` as `val publish`, and never fails the job.
13
+
14
+ Connected projects on a host of their own (for example Vercel) publish exactly as before.
15
+
16
+ - [#754](https://github.com/valbuild/val/pull/754) [`40e933d`](https://github.com/valbuild/val/commit/40e933da7fe01cfe1c3f4a43c156f0b2f8a11f62) Thanks [@freekh](https://github.com/freekh)! - Publishing a managed project is now a queued job, and a publish is either live or it did not happen.
17
+
18
+ Pressing **Publish** in a managed project no longer commits first and builds after. It asks the content service for a publish job and returns at once; the Studio tab that pressed builds the job and uploads it, and the content service checks the build renders and makes it live — recording the commit only then. So there is no longer a "Saved, not yet live" state to get stuck in, and no **Finish publishing** button to get out of it.
19
+
20
+ - A press made while another publish is going out is queued behind it, and built when its turn comes by whichever open Studio tab is free.
21
+ - A tab that closes after uploading costs nothing: the rest of the publish is the content service's.
22
+ - A publish that fails before it goes live says so, with **Try again** and **Discard changes**. Your changes are kept either way.
23
+ - Publishing from the site (a page that cannot build) still opens a Studio window to build it; the window now runs the publish job for the page.
24
+
25
+ Connected projects (with a repository) that are hosted on the Val platform publish as a queued job too — see the note on connected projects. Those hosted elsewhere, for example on Vercel, publish exactly as before.
26
+
27
+ For the Val server: the publish proxy now forwards the publish-job routes, `/api/val/publish-job-prepare` renders a job's sources and hands content its archive, and `/api/val/built-source` is removed.
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [[`57af895`](https://github.com/valbuild/val/commit/57af8959cc46519a405b1ed7441623f8bf2e6d27), [`5876fe0`](https://github.com/valbuild/val/commit/5876fe075fdee1f76f1cdaa2488ac52c58c894a8)]:
32
+ - @valbuild/core@0.138.0
33
+
34
+ ## 0.137.0
35
+
36
+ ### Minor Changes
37
+
38
+ - [#676](https://github.com/valbuild/val/pull/676) [`48ac9f0`](https://github.com/valbuild/val/commit/48ac9f06dd59a5b4ca60232b1ef198cf42f23aa6) Thanks [@freekh](https://github.com/freekh)! - A link-check endpoint, so the Studio can find out whether an external page is
39
+ still there.
40
+
41
+ `POST /external-urls/check` opens up to twenty URLs and reports what answered:
42
+ the final status after redirects, where it ended up, and how long it took. The
43
+ Studio's Check button in the external pages dialog is the only caller, and it
44
+ cannot do this itself — a cross-origin `fetch` from a browser cannot read a
45
+ response status without CORS headers no ordinary site sends.
46
+
47
+ It is an endpoint that makes outbound requests to addresses your content
48
+ supplies, so it is built as one:
49
+
50
+ - It is behind the same session check as every other authenticated route —
51
+ which, in fs mode, means the same anonymous access every other route allows
52
+ on a developer's own machine. It is not a new way in; it is the same door.
53
+ - It refuses to connect to anything that is not a public internet address —
54
+ loopback, link-local, the private ranges, the carrier-grade NAT range, and
55
+ the cloud metadata service that hands out instance credentials. The check is
56
+ on the RESOLVED address rather than the hostname, so a name that resolves to
57
+ `127.0.0.1` is refused, and it runs again at every redirect hop.
58
+ - It answers with one generic message for anything it refused, so the endpoint
59
+ cannot be used to find out what a hostname resolves to from inside your
60
+ network. The reason is logged on the server.
61
+ - `HEAD` first, `GET` only where a site answers 405 or 501 to it, and no
62
+ connection reuse between targets.
63
+
64
+ - [#746](https://github.com/valbuild/val/pull/746) [`e2bfc0e`](https://github.com/valbuild/val/commit/e2bfc0e40383fe25d3ec0473d1032f6264a8939f) Thanks [@freekh](https://github.com/freekh)! - A site hosted on Val (a managed project, with no repository of its own) can now be updated from the Studio. **Settings → Updates** says whether a newer version of the software the site runs on is available, lists the packages that move and their versions, and offers **Update site**.
65
+
66
+ Updating rebuilds the site in the browser on the new versions and publishes it through the ordinary publish, so it is checked before it goes live: if the updated site does not render, nothing changes and the site keeps running the old versions. Unpublished changes are kept and are **not** published by an update. After it goes live, reload the Studio to use the new one.
67
+
68
+ In Safari, which cannot build the site in the Studio's own page, Update site opens a tab that builds and publishes it, the same way a publish does there, and the Studio follows its progress.
69
+
70
+ A Studio that was opened before the site was updated no longer publishes: pressing Publish asks you to reload first, and nothing is saved until you do. Your unpublished changes are kept. Without this, a Studio left open from before an update would have built the site with the previous version of itself.
71
+
72
+ The versions come from the template the project was made from, as the platform last built it. A project whose code lives in a repository is not offered this; its dependencies are updated there.
73
+
74
+ For hosts: `@valbuild/server` forwards `GET` and `POST /api/val/publish-api/update-target` to the content service, beside `/build-target` and `/project-source`.
75
+
76
+ ### Patch Changes
77
+
78
+ - [#748](https://github.com/valbuild/val/pull/748) [`beda04b`](https://github.com/valbuild/val/commit/beda04bd326cbab18ed103e8d43917b16f43bd8c) Thanks [@freekh](https://github.com/freekh)! - A save is no longer refused once because an older answer from the server arrived after a newer one. The content service now reports a version of its list of changes, which goes up with every save, discard and publish. The Studio ignores any answer older than one it already has, including the answer to its own last save, discard or publish, so a discarded change can no longer briefly come back, and a published one can no longer briefly show as unpublished.
79
+ - Updated dependencies [[`43b58f2`](https://github.com/valbuild/val/commit/43b58f2962840883badf91950da2c51b89d1c7cb)]:
80
+ - @valbuild/core@0.137.0
81
+
3
82
  ## 0.136.10
4
83
 
5
84
  ### Patch Changes
@@ -424,6 +424,7 @@ export declare const Api: {
424
424
  mode: z.ZodUnion<readonly [z.ZodLiteral<"http">, z.ZodLiteral<"fs">]>;
425
425
  jsonEntriesSha: z.ZodOptional<z.ZodString>;
426
426
  sourceMode: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"managed">, z.ZodLiteral<"connected">]>>;
427
+ publishJobs: z.ZodOptional<z.ZodBoolean>;
427
428
  publishRefusal: z.ZodOptional<z.ZodObject<{
428
429
  code: z.ZodLiteral<"no-base">;
429
430
  message: z.ZodString;
@@ -440,6 +441,7 @@ export declare const Api: {
440
441
  appliedPatches: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>>;
441
442
  headCommitSha: z.ZodOptional<z.ZodString>;
442
443
  headPatchId: z.ZodOptional<z.ZodNullable<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>>;
444
+ headVersion: z.ZodOptional<z.ZodNumber>;
443
445
  commits: z.ZodArray<z.ZodObject<{
444
446
  commitSha: z.ZodString;
445
447
  clientCommitSha: z.ZodNullable<z.ZodString>;
@@ -469,6 +471,7 @@ export declare const Api: {
469
471
  profileId: z.ZodNullable<z.ZodString>;
470
472
  mode: z.ZodUnion<readonly [z.ZodLiteral<"http">, z.ZodLiteral<"fs">]>;
471
473
  sourceMode: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"managed">, z.ZodLiteral<"connected">]>>;
474
+ publishJobs: z.ZodOptional<z.ZodBoolean>;
472
475
  publishRefusal: z.ZodOptional<z.ZodObject<{
473
476
  code: z.ZodLiteral<"no-base">;
474
477
  message: z.ZodString;
@@ -567,6 +570,55 @@ export declare const Api: {
567
570
  }, z.core.$strip>]>;
568
571
  };
569
572
  };
573
+ /**
574
+ * Open a batch of external URLs and report what answered.
575
+ *
576
+ * A batch rather than one URL because the client checks a list, and a POST
577
+ * rather than a GET because the list is the input and does not belong in a
578
+ * URL or a cache.
579
+ *
580
+ * The server makes outbound requests to addresses this body supplies, which
581
+ * is SSRF by construction - see `linkCheck/addressGuard.ts` for the deny
582
+ * list that makes it safe, and `docs/plans/external-page-link-checks.md` for
583
+ * why it is a deny list on the RESOLVED address.
584
+ */
585
+ "/external-urls/check": {
586
+ POST: {
587
+ req: {
588
+ body: z.ZodObject<{
589
+ urls: z.ZodArray<z.ZodString>;
590
+ }, z.core.$strip>;
591
+ cookies: {
592
+ val_session: z.ZodOptional<z.ZodString>;
593
+ };
594
+ };
595
+ res: z.ZodUnion<readonly [z.ZodObject<{
596
+ status: z.ZodLiteral<401>;
597
+ json: z.ZodObject<{
598
+ message: z.ZodString;
599
+ }, z.core.$strip>;
600
+ }, z.core.$strip>, z.ZodObject<{
601
+ status: z.ZodLiteral<200>;
602
+ json: z.ZodObject<{
603
+ results: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
604
+ kind: z.ZodLiteral<"answered">;
605
+ code: z.ZodNumber;
606
+ finalUrl: z.ZodString;
607
+ ms: z.ZodNumber;
608
+ }, z.core.$strip>, z.ZodObject<{
609
+ kind: z.ZodLiteral<"unreachable">;
610
+ message: z.ZodString;
611
+ }, z.core.$strip>, z.ZodObject<{
612
+ kind: z.ZodLiteral<"timeout">;
613
+ ms: z.ZodNumber;
614
+ }, z.core.$strip>, z.ZodObject<{
615
+ kind: z.ZodLiteral<"skipped">;
616
+ message: z.ZodString;
617
+ }, z.core.$strip>]>>;
618
+ }, z.core.$strip>;
619
+ }, z.core.$strip>]>;
620
+ };
621
+ };
570
622
  "/direct-file-upload-settings": {
571
623
  POST: {
572
624
  req: {
@@ -608,6 +660,7 @@ export declare const Api: {
608
660
  };
609
661
  body: z.ZodOptional<z.ZodObject<{
610
662
  unstagePatchIds: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>>;
663
+ reportHeadVersion: z.ZodOptional<z.ZodLiteral<true>>;
611
664
  }, z.core.$strip>>;
612
665
  cookies: {
613
666
  val_session: z.ZodOptional<z.ZodString>;
@@ -632,7 +685,10 @@ export declare const Api: {
632
685
  }, z.core.$strip>;
633
686
  }, z.core.$strip>, z.ZodObject<{
634
687
  status: z.ZodLiteral<200>;
635
- json: z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>;
688
+ json: z.ZodUnion<readonly [z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>, z.ZodObject<{
689
+ deleted: z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>;
690
+ headVersion: z.ZodOptional<z.ZodNumber>;
691
+ }, z.core.$strip>]>;
636
692
  }, z.core.$strip>]>;
637
693
  };
638
694
  PUT: {
@@ -692,6 +748,7 @@ export declare const Api: {
692
748
  patchId: z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>;
693
749
  }, z.core.$strip>]>;
694
750
  patchGroupId: z.ZodOptional<z.ZodString>;
751
+ headVersion: z.ZodOptional<z.ZodNumber>;
695
752
  }, z.core.$strip>;
696
753
  }, z.core.$strip>]>;
697
754
  };
@@ -1023,24 +1080,14 @@ export declare const Api: {
1023
1080
  }, z.core.$strip>]>;
1024
1081
  };
1025
1082
  };
1026
- /**
1027
- * The `.val.ts` text of every module that changed since the RUNNING build,
1028
- * with every commit since it applied, for a build made in the browser.
1029
- *
1030
- * A Studio build starts from the source stored with the live build. A commit
1031
- * whose own publish failed -- or the first commit of a project that started
1032
- * as a copy of a template -- is in content and in no stored source, so a build
1033
- * that added only its own save's files shipped without it. Only this server
1034
- * can render those commits back into text: content keeps a managed project's
1035
- * content as Source, not as files.
1036
- *
1037
- * Committed patches only. A pending one is somebody's unpublished work, and
1038
- * is not this build's to ship. 409 where there is nothing to answer with: a
1039
- * project with a repository, or a build that did not embed its source.
1040
- */
1041
- "/built-source": {
1042
- GET: {
1083
+ "/publish-job-prepare": {
1084
+ POST: {
1043
1085
  req: {
1086
+ body: z.ZodObject<{
1087
+ jobId: z.ZodString;
1088
+ tab: z.ZodString;
1089
+ patchIds: z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>;
1090
+ }, z.core.$strip>;
1044
1091
  cookies: {
1045
1092
  val_session: z.ZodOptional<z.ZodString>;
1046
1093
  };
@@ -1050,6 +1097,11 @@ export declare const Api: {
1050
1097
  json: z.ZodObject<{
1051
1098
  message: z.ZodString;
1052
1099
  }, z.core.$strip>;
1100
+ }, z.core.$strip>, z.ZodObject<{
1101
+ status: z.ZodLiteral<400>;
1102
+ json: z.ZodObject<{
1103
+ message: z.ZodString;
1104
+ }, z.core.$strip>;
1053
1105
  }, z.core.$strip>, z.ZodObject<{
1054
1106
  status: z.ZodLiteral<409>;
1055
1107
  json: z.ZodObject<{
@@ -1060,10 +1112,28 @@ export declare const Api: {
1060
1112
  json: z.ZodObject<{
1061
1113
  message: z.ZodString;
1062
1114
  }, z.core.$strip>;
1115
+ }, z.core.$strip>, z.ZodObject<{
1116
+ status: z.ZodLiteral<502>;
1117
+ json: z.ZodObject<{
1118
+ message: z.ZodString;
1119
+ }, z.core.$strip>;
1063
1120
  }, z.core.$strip>, z.ZodObject<{
1064
1121
  status: z.ZodLiteral<200>;
1065
1122
  json: z.ZodObject<{
1066
- files: z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>;
1123
+ job: z.ZodNullable<z.ZodObject<{
1124
+ id: z.ZodString;
1125
+ step: z.ZodNullable<z.ZodEnum<{
1126
+ prepare: "prepare";
1127
+ build: "build";
1128
+ upload: "upload";
1129
+ }>>;
1130
+ base: z.ZodNullable<z.ZodString>;
1131
+ patches: z.ZodArray<z.ZodString>;
1132
+ }, z.core.$strip>>;
1133
+ sourceFiles: z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>;
1134
+ binaryFiles: z.ZodRecord<z.ZodString, z.ZodString>;
1135
+ binaryFilesUnread: z.ZodArray<z.ZodString>;
1136
+ branch: z.ZodNullable<z.ZodString>;
1067
1137
  }, z.core.$strip>;
1068
1138
  }, z.core.$strip>]>;
1069
1139
  };
@@ -1131,6 +1201,7 @@ export declare const Api: {
1131
1201
  moduleFilePath: z.ZodString & z.ZodType<ModuleFilePath, string, z.core.$ZodTypeInternals<ModuleFilePath, string>>;
1132
1202
  message: z.ZodString;
1133
1203
  }, z.core.$strip>>>;
1204
+ headVersion: z.ZodOptional<z.ZodNumber>;
1134
1205
  }, z.core.$strip>;
1135
1206
  }, z.core.$strip>, z.ZodObject<{
1136
1207
  status: z.ZodLiteral<403>;
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * The publish API's types, COPIED from the service that serves them.
3
3
  *
4
- * Source: `content/src/handlers/Api.ts` in valbuild/home, branch
5
- * `claude/new-project-studio-saves-0g9ksb`, commit `ecf3b8d`. The routes below
6
- * are that file's `/publish*` entries and the two types they use, verbatim,
7
- * comments included, so that the two can be diffed by eye.
4
+ * Source: `content/src/handlers/Api.ts` in valbuild/home. The routes below
5
+ * are that file's `/publish*` entries and the types they use, verbatim,
6
+ * comments included, so that the two can be diffed by eye: the publish
7
+ * lifecycle from branch `claude/new-project-studio-saves-0g9ksb`, commit
8
+ * `ecf3b8d`; the publish-job routes (`/publish-requests`, `/publish-jobs`)
9
+ * and `PublishRequestStatus` / `PublishTabJob` from `main`, commit `acb4a49`, with `/ci-runs`.
8
10
  *
9
11
  * **Copied rather than imported, because it cannot be imported.** That file
10
12
  * lives in a private repository which publishes nothing to npm, and its own
@@ -23,6 +25,7 @@
23
25
  * A copy is not a guarantee, only a tripwire: nothing checks it against the
24
26
  * service, and the parsers in `protocol.ts` are what actually holds at runtime.
25
27
  */
28
+ import type { Json } from "@valbuild/core";
26
29
  export type ContentPublishApi = {
27
30
  /**
28
31
  * Publishing a build, as a resource with a lifecycle.
@@ -175,6 +178,173 @@ export type ContentPublishApi = {
175
178
  };
176
179
  };
177
180
  };
181
+ /**
182
+ * Publishing as a queued job: a press of Publish is a REQUEST, and returns
183
+ * at once. A job takes every queued request and everything pending; the tab
184
+ * that pressed runs its prepare (through the Val server), build and upload,
185
+ * reporting each step; then content runs verify and the seal itself. See
186
+ * docs/app-mode.md, "Publishing is a queued job".
187
+ *
188
+ * ```
189
+ * POST /publish-requests press -> the request, and a job to build
190
+ * GET /publish-requests/{id} where it is: queued, publishing, live, failed
191
+ * POST /publish-requests/try-again Try again: resume a paused queue, press anew
192
+ * POST /publish-jobs/next a free tab asks for queued work
193
+ * POST /publish-jobs/{id}/prepare the job's sources, archived (the Val server's)
194
+ * POST /publish-jobs/{id}/steps the tab reports build and upload
195
+ * POST /publish-jobs/{id}/renew the tab is still building
196
+ * POST /publish-jobs/{id}/cancel Cancel, before the seal
197
+ * POST /publish-jobs/{id}/discard Discard these changes, on a failed job
198
+ * ```
199
+ *
200
+ * Every id the Studio sends -- a request's, a tab's -- is its own, minted by
201
+ * it, `[A-Za-z0-9_-]{1,100}`. A press is idempotent on its request id; a step
202
+ * report names its step, so one retried after a lost answer does nothing
203
+ * twice.
204
+ */
205
+ /**
206
+ * CI's own report of how a build of a git commit went, sent by the
207
+ * connected workflow's last step with its project token. See `ciRuns.ts`.
208
+ */
209
+ "/ci-runs": {
210
+ POST: {
211
+ body: {
212
+ commit: string;
213
+ branch?: string;
214
+ status: "failed" | "succeeded";
215
+ url?: string;
216
+ };
217
+ res: {
218
+ recorded: true;
219
+ };
220
+ };
221
+ };
222
+ "/ci-runs/newest": {
223
+ GET: {
224
+ res: {
225
+ run: {
226
+ commit: string;
227
+ status: "failed" | "succeeded";
228
+ url: string | null;
229
+ } | null;
230
+ };
231
+ };
232
+ };
233
+ "/publish-requests": {
234
+ POST: {
235
+ body: {
236
+ requestId: string;
237
+ tab: string;
238
+ };
239
+ res: {
240
+ request: PublishRequestStatus;
241
+ job: PublishTabJob | null;
242
+ };
243
+ };
244
+ };
245
+ "/publish-requests/try-again": {
246
+ POST: {
247
+ body: {
248
+ requestId: string;
249
+ tab: string;
250
+ };
251
+ res: {
252
+ request: PublishRequestStatus;
253
+ job: PublishTabJob | null;
254
+ };
255
+ };
256
+ };
257
+ "/publish-requests/:requestId": {
258
+ GET: {
259
+ res: {
260
+ request: PublishRequestStatus;
261
+ };
262
+ };
263
+ };
264
+ "/publish-jobs/next": {
265
+ POST: {
266
+ body: {
267
+ tab: string;
268
+ };
269
+ res: {
270
+ job: PublishTabJob | null;
271
+ };
272
+ };
273
+ };
274
+ "/publish-jobs/:jobId/prepare": {
275
+ POST: {
276
+ /**
277
+ * `/commit`'s body, less what names a commit: the job's commit is minted
278
+ * at the seal. `modules` is required here -- a managed commit's archive,
279
+ * written from it, is the only copy of what it published.
280
+ */
281
+ body: {
282
+ tab: string;
283
+ root: string;
284
+ filesDirectory?: string;
285
+ patchedSourceFiles: Record<string, string | null>;
286
+ patchedBinaryFilesDescriptors: Record<string, {
287
+ patchId: string;
288
+ remote?: boolean;
289
+ }>;
290
+ modules: Record<string, {
291
+ source: Json;
292
+ schema: Json;
293
+ }>;
294
+ /**
295
+ * Connected: the git commit the Studio's deployment was built from. A
296
+ * branch whose tip is that commit has nothing it has not seen.
297
+ */
298
+ gitCommit?: string;
299
+ };
300
+ res: {
301
+ job: PublishTabJob | null;
302
+ };
303
+ };
304
+ };
305
+ "/publish-jobs/:jobId/steps": {
306
+ POST: {
307
+ body: {
308
+ tab: string;
309
+ step: "prepare" | "build" | "upload";
310
+ ok: boolean;
311
+ /** A finished build step: the id of the publish (`POST /publish`) it declared. */
312
+ build?: string;
313
+ };
314
+ res: {
315
+ job: PublishTabJob | null;
316
+ };
317
+ };
318
+ };
319
+ "/publish-jobs/:jobId/renew": {
320
+ POST: {
321
+ body: {
322
+ tab: string;
323
+ };
324
+ res: {
325
+ renewed: boolean;
326
+ };
327
+ };
328
+ };
329
+ "/publish-jobs/:jobId/cancel": {
330
+ POST: {
331
+ res: {
332
+ cancelled: boolean;
333
+ };
334
+ };
335
+ };
336
+ "/publish-jobs/:jobId/discard": {
337
+ POST: {
338
+ /** The forward closure, as `DELETE /patches` takes it. */
339
+ body: {
340
+ unstagePatchIds?: string[];
341
+ };
342
+ /** Changes it could not discard: another job in flight holds them. */
343
+ res: {
344
+ stillHeld: string[];
345
+ };
346
+ };
347
+ };
178
348
  };
179
349
  /**
180
350
  * Where a publish is. Mirrors `PublishState` in `utils/publishPlan.ts`, which
@@ -199,3 +369,36 @@ export type PublishProblem = {
199
369
  hint?: string;
200
370
  keys?: string[];
201
371
  };
372
+ /**
373
+ * Where a press of Publish is. `live` names the commit that carries it -- its
374
+ * own, or a newer one that contains it; `failed` names what the editor can do.
375
+ * The same shape as `RequestStatus` in `utils/jobPlan.ts`, which decides it.
376
+ */
377
+ export type PublishRequestStatus = {
378
+ kind: "queued";
379
+ } | {
380
+ kind: "publishing";
381
+ } | {
382
+ kind: "live";
383
+ commit: string;
384
+ } | {
385
+ kind: "failed";
386
+ message: string;
387
+ actions: ("try-again" | "discard" | "re-run-build")[];
388
+ /** The job that failed: what Discard is pressed on. */
389
+ job: string;
390
+ } | {
391
+ kind: "cancelled";
392
+ } | {
393
+ kind: "nothing-to-publish";
394
+ };
395
+ /** What a tab needs to know about a job it is running. */
396
+ export type PublishTabJob = {
397
+ id: string;
398
+ /** The step the tab is to run next; null once the job is content's. */
399
+ step: "prepare" | "build" | "upload" | null;
400
+ /** The recorded commit its build is based on. */
401
+ base: string | null;
402
+ /** The changes its build is to include, on top of `base`. */
403
+ patches: string[];
404
+ };
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { ContentPublishApi, PublishProblem, PublishState } from "./contentApi.js";
2
+ import { ContentPublishApi, PublishProblem, PublishRequestStatus, PublishState, PublishTabJob } from "./contentApi.js";
3
3
  /**
4
4
  * Reading what content answers.
5
5
  *
@@ -115,8 +115,105 @@ declare const projectSourceResponse: z.ZodObject<{
115
115
  publicPaths: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
116
116
  }, z.core.$strip>;
117
117
  export type ProjectSourceResponse = z.infer<typeof projectSourceResponse>;
118
+ /**
119
+ * Whether a managed project can be updated onto its template's current seed,
120
+ * and -- when it is asked with `POST`, the press -- what to build against.
121
+ *
122
+ * An update rebuilds the site as it is on newer dependencies. The loader
123
+ * decides whether there is one; content adds whether the project is managed at
124
+ * all, and the branch head the rebuild is wired at, because an update
125
+ * publishes no content of its own.
126
+ *
127
+ * `target` is a `/build-target` answer whose project half is the SEED's
128
+ * dependency layer, already copied into the project so a publish can name it.
129
+ * Only a `POST` carries it.
130
+ */
131
+ declare const dependencyChange: z.ZodObject<{
132
+ name: z.ZodString;
133
+ section: z.ZodEnum<{
134
+ dependencies: "dependencies";
135
+ devDependencies: "devDependencies";
136
+ }>;
137
+ from: z.ZodNullable<z.ZodString>;
138
+ to: z.ZodNullable<z.ZodString>;
139
+ }, z.core.$strip>;
140
+ declare const updateTargetResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
141
+ status: z.ZodLiteral<"current">;
142
+ template: z.ZodString;
143
+ layerRev: z.ZodString;
144
+ }, z.core.$strip>, z.ZodObject<{
145
+ status: z.ZodLiteral<"available">;
146
+ template: z.ZodString;
147
+ changes: z.ZodArray<z.ZodObject<{
148
+ name: z.ZodString;
149
+ section: z.ZodEnum<{
150
+ dependencies: "dependencies";
151
+ devDependencies: "devDependencies";
152
+ }>;
153
+ from: z.ZodNullable<z.ZodString>;
154
+ to: z.ZodNullable<z.ZodString>;
155
+ }, z.core.$strip>>;
156
+ layerRev: z.ZodObject<{
157
+ from: z.ZodNullable<z.ZodString>;
158
+ to: z.ZodString;
159
+ }, z.core.$strip>;
160
+ packageJson: z.ZodString;
161
+ commit: z.ZodNullable<z.ZodString>;
162
+ branch: z.ZodString;
163
+ target: z.ZodOptional<z.ZodObject<{
164
+ base: z.ZodObject<{
165
+ rev: z.ZodString;
166
+ shellRev: z.ZodString;
167
+ rscShellRev: z.ZodString;
168
+ modules: z.ZodRecord<z.ZodString, z.ZodString>;
169
+ rscModules: z.ZodRecord<z.ZodString, z.ZodString>;
170
+ paths: z.ZodObject<{
171
+ baseFromProject: z.ZodString;
172
+ projectVendorDir: z.ZodString;
173
+ rscVendorBase: z.ZodString;
174
+ rscRuntimeSpecifier: z.ZodString;
175
+ rscRuntimePath: z.ZodString;
176
+ flightServer: z.ZodString;
177
+ serverFnBase: z.ZodString;
178
+ }, z.core.$strip>;
179
+ }, z.core.$strip>;
180
+ project: z.ZodObject<{
181
+ rev: z.ZodNullable<z.ZodString>;
182
+ rsc: z.ZodBoolean;
183
+ modules: z.ZodRecord<z.ZodString, z.ZodString>;
184
+ css: z.ZodRecord<z.ZodString, z.ZodString>;
185
+ workerOnly: z.ZodArray<z.ZodString>;
186
+ }, z.core.$strip>;
187
+ }, z.core.$strip>>;
188
+ }, z.core.$strip>, z.ZodObject<{
189
+ status: z.ZodLiteral<"unavailable">;
190
+ reason: z.ZodString;
191
+ message: z.ZodString;
192
+ }, z.core.$strip>], "status">;
193
+ export type UpdateTargetResponse = z.infer<typeof updateTargetResponse>;
194
+ export type DependencyChange = z.infer<typeof dependencyChange>;
195
+ export declare const parseUpdateTarget: (body: unknown) => UpdateTargetResponse;
118
196
  export declare const parseBuildTarget: (body: unknown) => BuildTargetResponse;
119
197
  export declare const parseProjectSource: (body: unknown) => ProjectSourceResponse;
198
+ export type { PublishRequestStatus, PublishTabJob };
199
+ export type PressResponse = ContentPublishApi["/publish-requests"]["POST"]["res"];
200
+ export type RequestStatusResponse = ContentPublishApi["/publish-requests/:requestId"]["GET"]["res"];
201
+ export type NextJobResponse = ContentPublishApi["/publish-jobs/next"]["POST"]["res"];
202
+ export type JobPrepareBody = ContentPublishApi["/publish-jobs/:jobId/prepare"]["POST"]["body"];
203
+ export type JobStepBody = ContentPublishApi["/publish-jobs/:jobId/steps"]["POST"]["body"];
204
+ export type JobStepResponse = ContentPublishApi["/publish-jobs/:jobId/steps"]["POST"]["res"];
205
+ export type JobRenewResponse = ContentPublishApi["/publish-jobs/:jobId/renew"]["POST"]["res"];
206
+ export type JobCancelResponse = ContentPublishApi["/publish-jobs/:jobId/cancel"]["POST"]["res"];
207
+ export type JobDiscardResponse = ContentPublishApi["/publish-jobs/:jobId/discard"]["POST"]["res"];
208
+ export declare const parsePress: (body: unknown, call: string) => PressResponse;
209
+ export declare const parseRequestStatus: (body: unknown) => RequestStatusResponse;
210
+ export declare const parseJob: (body: unknown, call: string) => JobStepResponse;
211
+ export declare const parseRenew: (body: unknown) => JobRenewResponse;
212
+ export declare const parseCancel: (body: unknown) => JobCancelResponse;
213
+ export declare const parseDiscard: (body: unknown) => JobDiscardResponse;
214
+ export type CiRunReportBody = ContentPublishApi["/ci-runs"]["POST"]["body"];
215
+ export type NewestCiRunResponse = ContentPublishApi["/ci-runs/newest"]["GET"]["res"];
216
+ export declare const parseNewestCiRun: (body: unknown) => NewestCiRunResponse;
120
217
  /**
121
218
  * The `details` of a refusal, when it carries a list of problems.
122
219
  *