@valbuild/shared 0.150.16 → 0.150.18

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.150.18
4
+
5
+ ### Patch Changes
6
+
7
+ - [#994](https://github.com/valbuild/val/pull/994) [`43f72c8`](https://github.com/valbuild/val/commit/43f72c85c2114fe9a0ce536204858d2d3c05f962) Thanks [@freekh](https://github.com/freekh)! - **Proposals can be built by Val Build instead of in your browser.** When Val Build builds your project's proposals, the design tab's **Build** and the assistant's `build_proposal` in the design tab ask it to build the proposal, and the Studio waits until the build is served at the proposal's address. A small code change is built, checked and on the proposal's page in a few seconds, against around twenty for the build alone in the browser. Nothing is downloaded first, no builder tab opens on an iPhone or in Safari, and closing the tab does not stop the build.
8
+
9
+ Val Build is turning this on project by project, in the design tab first. Until it is on for yours, Build works as before, in the browser, and so does Build in the proposals bar.
10
+
11
+ ## 0.150.17
12
+
13
+ ### Patch Changes
14
+
15
+ - [#992](https://github.com/valbuild/val/pull/992) [`ea3bf9b`](https://github.com/valbuild/val/commit/ea3bf9bbacd22bbb5485fd798d6b3a2d5916be81) Thanks [@freekh](https://github.com/freekh)! - Content analytics now counts on self-hosted sites (Custom hosting), not only on sites Val hosts.
16
+
17
+ A self-hosted app's own Val server now hands each analytics beacon on to Val's ingest, which counts it. To turn it on:
18
+
19
+ - Run the app with Val's content service: `VAL_API_KEY`, `VAL_SECRET` and `VAL_PROJECT` set. Most deployed apps already do.
20
+ - Set `VAL_ANALYTICS=true`. In TanStack Start, read it in your layout's loader (`{ enabled: process.env.VAL_ANALYTICS === "true" }`) and pass the result to `<ValProvider analytics>`. A self-hosted site has no platform to pass it for you.
21
+ - Switch analytics on for the project in Val's admin. While it is off there, the script on your pages is told so and stops sending.
22
+
23
+ The server forwards only same-origin beacons from a browser. It passes on the visitor's User-Agent and, where your host provides one (Vercel, Cloudflare or CloudFront), their country. It never passes on their IP address. If the api key is not accepted, the server logs one warning naming `VAL_API_KEY` and `VAL_PROJECT`. Local development (fs mode) works as before and counts nothing.
24
+
25
+ `VAL_ANALYTICS_INGEST_URL` overrides where beacons are sent. You will normally not need it.
26
+
3
27
  ## 0.150.16
4
28
 
5
29
  ### Patch Changes
@@ -176,6 +176,13 @@ export declare const Api: {
176
176
  POST: {
177
177
  req: {
178
178
  body: z.ZodUnknown;
179
+ headers: {
180
+ "sec-fetch-site": z.ZodOptional<z.ZodString>;
181
+ "user-agent": z.ZodOptional<z.ZodString>;
182
+ "x-vercel-ip-country": z.ZodOptional<z.ZodString>;
183
+ "cf-ipcountry": z.ZodOptional<z.ZodString>;
184
+ "cloudfront-viewer-country": z.ZodOptional<z.ZodString>;
185
+ };
179
186
  };
180
187
  res: z.ZodUnion<readonly [z.ZodObject<{
181
188
  status: z.ZodLiteral<200>;
@@ -187,6 +194,11 @@ export declare const Api: {
187
194
  json: z.ZodObject<{
188
195
  message: z.ZodString;
189
196
  }, z.core.$strip>;
197
+ }, z.core.$strip>, z.ZodObject<{
198
+ status: z.ZodLiteral<410>;
199
+ json: z.ZodObject<{
200
+ message: z.ZodString;
201
+ }, z.core.$strip>;
190
202
  }, z.core.$strip>]>;
191
203
  };
192
204
  };
@@ -10,6 +10,16 @@ export type BuiltProposal = {
10
10
  hash: string;
11
11
  inputs?: string | null;
12
12
  } | null;
13
+ /**
14
+ * A build the platform was asked for and has not served
15
+ * (`buildProposalOnServer`): `pending`, or `failed` with why. Absent from
16
+ * content that does not build.
17
+ */
18
+ serverBuild?: {
19
+ state: "pending" | "failed";
20
+ inputs: string;
21
+ error: string | null;
22
+ } | null;
13
23
  };
14
24
  /** The calls a build of a proposal makes. An error carries content's `status`. */
15
25
  export type ProposalBuildClient<P extends BuiltProposal> = {
@@ -119,3 +129,55 @@ export declare function buildProposal<P extends BuiltProposal>(deps: {
119
129
  now?: () => number;
120
130
  sleep?: (ms: number) => Promise<void>;
121
131
  }): Promise<ProposalBuildOutcome<P>>;
132
+ /** What content answers, in `details.code`, for a project it does not build yet. */
133
+ export declare const SERVER_BUILDS_OFF = "server-builds-off";
134
+ /** How often the proposal is read while the platform builds it. */
135
+ export declare const SERVER_BUILD_POLL_MS = 1000;
136
+ /**
137
+ * How long a build by the platform is waited for. It is seconds
138
+ * (valbuild/home `docs/plans/server-builds.md`); this is for the platform not
139
+ * answering, never for a build that is merely slow.
140
+ */
141
+ export declare const SERVER_BUILD_WAIT_MS: number;
142
+ /**
143
+ * Where a build by the platform is (`buildProposalOnServer`): reading what is
144
+ * staged, then Val Build building, rendering and serving it. Its own type,
145
+ * not `ProposalBuildStep`'s, which every page that runs `buildProposal`
146
+ * switches over.
147
+ */
148
+ export type ServerBuildStep = {
149
+ kind: "reading";
150
+ } | {
151
+ kind: "on-server";
152
+ since: number;
153
+ };
154
+ /** The calls a build by the platform makes. An error carries content's `status`. */
155
+ export type ServerBuildClient<P extends BuiltProposal> = Pick<ProposalBuildClient<P>, "get"> & {
156
+ /** Ask the platform to build the staged files `inputs` names: `/build` without a publish. */
157
+ askBuild(name: string, inputs: string): Promise<P>;
158
+ };
159
+ /**
160
+ * Have the platform build the proposal's staged files and serve the build at
161
+ * its address (valbuild/home `docs/plans/server-builds.md`): nothing is built
162
+ * in this tab, so no bundler is loaded, no builder tab is opened, and a tab
163
+ * that closes meanwhile loses nothing.
164
+ *
165
+ * Content answers at once and builds in a job; the proposal is read until it
166
+ * says the build of these inputs is served, or why it is not. `off` when
167
+ * content does not build this project yet: `buildProposal` builds it in the
168
+ * tab, as before.
169
+ *
170
+ * A cancel stops the waiting, not the build: the platform finishes it, and
171
+ * the proposal's address serves it when it renders.
172
+ */
173
+ export declare function buildProposalOnServer<P extends BuiltProposal>(deps: {
174
+ name: string;
175
+ client: ServerBuildClient<P>;
176
+ onStep?: (step: ServerBuildStep) => void;
177
+ signal?: AbortSignal;
178
+ /** For tests: the clock, and waiting on it. */
179
+ now?: () => number;
180
+ sleep?: (ms: number) => Promise<void>;
181
+ }): Promise<ProposalBuildOutcome<P> | {
182
+ status: "off";
183
+ }>;
@@ -19,6 +19,6 @@ export { DEPLOY_STOPPED_MESSAGE, carriedInto, runStudioDeploy, splitBinaryFiles,
19
19
  export { STUDIO_OUT_OF_DATE, STUDIO_OUT_OF_DATE_MESSAGE, layerMoved, loadedLayer, markStudioOutOfDate, readLiveLayer, rememberLoadedLayer, resetLoadedLayerForTests, siteMovedSinceLoad, studioOutOfDateRefusals, subscribeStudioOutOfDate, type LayerReading, } from "./loadedLayer.js";
20
20
  export { MergePrepareAnswer, createStudioJobClient, isMergeChange, type PreparedJob, type StudioJobClient, } from "./jobClient.js";
21
21
  export { RENEW_EVERY_MS, runJobToEnd, runStudioJob, type JobPhase, type StudioJobResult, } from "./runStudioJob.js";
22
- export { BUILD_CANCELLED_MESSAGE, SERVE_POLL_MS, SERVE_WAIT_MS, buildProposal, withoutLockfiles, type BuiltProposal, type ProposalBuildClient, type ProposalBuildOutcome, type ProposalBuildStep, } from "./buildProposal.js";
22
+ export { BUILD_CANCELLED_MESSAGE, SERVER_BUILDS_OFF, SERVER_BUILD_POLL_MS, SERVER_BUILD_WAIT_MS, SERVE_POLL_MS, SERVE_WAIT_MS, buildProposal, buildProposalOnServer, withoutLockfiles, type BuiltProposal, type ServerBuildClient, type ServerBuildStep, type ProposalBuildClient, type ProposalBuildOutcome, type ProposalBuildStep, } from "./buildProposal.js";
23
23
  export { proposalProject } from "./proposalProject.js";
24
24
  export { buildQuickPreview, clientFilesOf, type QuickPreviewOutcome, } from "./buildQuickPreview.js";
@@ -1754,11 +1754,22 @@ var Api = {
1754
1754
  // Parsed by the handler rather than here, so a beacon of a version this
1755
1755
  // server does not know is ignored instead of refused.
1756
1756
  req: {
1757
- body: zod.z.unknown()
1757
+ body: zod.z.unknown(),
1758
+ // What a server that forwards beacons to Val's ingest has to check
1759
+ // and pass on itself: a forward loses the browser's own headers. The
1760
+ // visitor's IP is deliberately not one of them.
1761
+ headers: {
1762
+ "sec-fetch-site": zod.z.string().optional(),
1763
+ "user-agent": zod.z.string().optional(),
1764
+ "x-vercel-ip-country": zod.z.string().optional(),
1765
+ "cf-ipcountry": zod.z.string().optional(),
1766
+ "cloudfront-viewer-country": zod.z.string().optional()
1767
+ }
1758
1768
  },
1759
1769
  res: zod.z.union([
1760
- // `counted` is false wherever nothing stores the counts -- every
1761
- // server but the hosted platform's, for now.
1770
+ // `counted` is true only where the beacon was handed to Val's ingest
1771
+ // and it counted it: the hosted platform, or a self-hosted server in
1772
+ // http mode. False everywhere else, local dev included.
1762
1773
  zod.z.object({
1763
1774
  status: zod.z.literal(200),
1764
1775
  json: zod.z.object({
@@ -1767,6 +1778,12 @@ var Api = {
1767
1778
  }), zod.z.object({
1768
1779
  status: zod.z.literal(400),
1769
1780
  json: GenericError
1781
+ }),
1782
+ // Analytics is switched off for this project in Val's admin. The
1783
+ // browser script stops sending when it sees this.
1784
+ zod.z.object({
1785
+ status: zod.z.literal(410),
1786
+ json: GenericError
1770
1787
  })])
1771
1788
  }
1772
1789
  },
@@ -1754,11 +1754,22 @@ var Api = {
1754
1754
  // Parsed by the handler rather than here, so a beacon of a version this
1755
1755
  // server does not know is ignored instead of refused.
1756
1756
  req: {
1757
- body: zod.z.unknown()
1757
+ body: zod.z.unknown(),
1758
+ // What a server that forwards beacons to Val's ingest has to check
1759
+ // and pass on itself: a forward loses the browser's own headers. The
1760
+ // visitor's IP is deliberately not one of them.
1761
+ headers: {
1762
+ "sec-fetch-site": zod.z.string().optional(),
1763
+ "user-agent": zod.z.string().optional(),
1764
+ "x-vercel-ip-country": zod.z.string().optional(),
1765
+ "cf-ipcountry": zod.z.string().optional(),
1766
+ "cloudfront-viewer-country": zod.z.string().optional()
1767
+ }
1758
1768
  },
1759
1769
  res: zod.z.union([
1760
- // `counted` is false wherever nothing stores the counts -- every
1761
- // server but the hosted platform's, for now.
1770
+ // `counted` is true only where the beacon was handed to Val's ingest
1771
+ // and it counted it: the hosted platform, or a self-hosted server in
1772
+ // http mode. False everywhere else, local dev included.
1762
1773
  zod.z.object({
1763
1774
  status: zod.z.literal(200),
1764
1775
  json: zod.z.object({
@@ -1767,6 +1778,12 @@ var Api = {
1767
1778
  }), zod.z.object({
1768
1779
  status: zod.z.literal(400),
1769
1780
  json: GenericError
1781
+ }),
1782
+ // Analytics is switched off for this project in Val's admin. The
1783
+ // browser script stops sending when it sees this.
1784
+ zod.z.object({
1785
+ status: zod.z.literal(410),
1786
+ json: GenericError
1770
1787
  })])
1771
1788
  }
1772
1789
  },
@@ -1747,11 +1747,22 @@ var Api = {
1747
1747
  // Parsed by the handler rather than here, so a beacon of a version this
1748
1748
  // server does not know is ignored instead of refused.
1749
1749
  req: {
1750
- body: z.unknown()
1750
+ body: z.unknown(),
1751
+ // What a server that forwards beacons to Val's ingest has to check
1752
+ // and pass on itself: a forward loses the browser's own headers. The
1753
+ // visitor's IP is deliberately not one of them.
1754
+ headers: {
1755
+ "sec-fetch-site": z.string().optional(),
1756
+ "user-agent": z.string().optional(),
1757
+ "x-vercel-ip-country": z.string().optional(),
1758
+ "cf-ipcountry": z.string().optional(),
1759
+ "cloudfront-viewer-country": z.string().optional()
1760
+ }
1751
1761
  },
1752
1762
  res: z.union([
1753
- // `counted` is false wherever nothing stores the counts -- every
1754
- // server but the hosted platform's, for now.
1763
+ // `counted` is true only where the beacon was handed to Val's ingest
1764
+ // and it counted it: the hosted platform, or a self-hosted server in
1765
+ // http mode. False everywhere else, local dev included.
1755
1766
  z.object({
1756
1767
  status: z.literal(200),
1757
1768
  json: z.object({
@@ -1760,6 +1771,12 @@ var Api = {
1760
1771
  }), z.object({
1761
1772
  status: z.literal(400),
1762
1773
  json: GenericError
1774
+ }),
1775
+ // Analytics is switched off for this project in Val's admin. The
1776
+ // browser script stops sending when it sees this.
1777
+ z.object({
1778
+ status: z.literal(410),
1779
+ json: GenericError
1763
1780
  })])
1764
1781
  }
1765
1782
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valbuild/shared",
3
- "version": "0.150.16",
3
+ "version": "0.150.18",
4
4
  "private": false,
5
5
  "description": "Val shared types and utilities",
6
6
  "repository": {
@@ -2309,6 +2309,8 @@ var SERVE_POLL_MS = 5000;
2309
2309
  function buildProposal(_x) {
2310
2310
  return _buildProposal.apply(this, arguments);
2311
2311
  }
2312
+
2313
+ /** What content answers, in `details.code`, for a project it does not build yet. */
2312
2314
  function _buildProposal() {
2313
2315
  _buildProposal = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee3(deps) {
2314
2316
  var _deps$now, _deps$sleep, _deps$onStep;
@@ -2569,6 +2571,257 @@ function _buildProposal() {
2569
2571
  }));
2570
2572
  return _buildProposal.apply(this, arguments);
2571
2573
  }
2574
+ var SERVER_BUILDS_OFF = "server-builds-off";
2575
+
2576
+ /** How often the proposal is read while the platform builds it. */
2577
+ var SERVER_BUILD_POLL_MS = 1000;
2578
+
2579
+ /**
2580
+ * How long a build by the platform is waited for. It is seconds
2581
+ * (valbuild/home `docs/plans/server-builds.md`); this is for the platform not
2582
+ * answering, never for a build that is merely slow.
2583
+ */
2584
+ var SERVER_BUILD_WAIT_MS = 4 * 60000;
2585
+
2586
+ /**
2587
+ * Where a build by the platform is (`buildProposalOnServer`): reading what is
2588
+ * staged, then Val Build building, rendering and serving it. Its own type,
2589
+ * not `ProposalBuildStep`'s, which every page that runs `buildProposal`
2590
+ * switches over.
2591
+ */
2592
+
2593
+ /** The calls a build by the platform makes. An error carries content's `status`. */
2594
+
2595
+ /**
2596
+ * Whether an error is content saying it does not build this project yet --
2597
+ * or not knowing the request: content from before it built anything refuses
2598
+ * a build without a publish as an invalid body (400). Either way the tab
2599
+ * builds it, as it always did.
2600
+ */
2601
+ function serverBuildsOff(error) {
2602
+ var status = statusOf(error);
2603
+ if (status === 400) return true;
2604
+ if (status !== 409) return false;
2605
+ var body = createForOfIteratorHelper._typeof(error) === "object" && error !== null && "body" in error ? error.body : null;
2606
+ var details = createForOfIteratorHelper._typeof(body) === "object" && body !== null && "details" in body ? body.details : null;
2607
+ return createForOfIteratorHelper._typeof(details) === "object" && details !== null && "code" in details && details.code === SERVER_BUILDS_OFF;
2608
+ }
2609
+
2610
+ /**
2611
+ * Have the platform build the proposal's staged files and serve the build at
2612
+ * its address (valbuild/home `docs/plans/server-builds.md`): nothing is built
2613
+ * in this tab, so no bundler is loaded, no builder tab is opened, and a tab
2614
+ * that closes meanwhile loses nothing.
2615
+ *
2616
+ * Content answers at once and builds in a job; the proposal is read until it
2617
+ * says the build of these inputs is served, or why it is not. `off` when
2618
+ * content does not build this project yet: `buildProposal` builds it in the
2619
+ * tab, as before.
2620
+ *
2621
+ * A cancel stops the waiting, not the build: the platform finishes it, and
2622
+ * the proposal's address serves it when it renders.
2623
+ */
2624
+ function buildProposalOnServer(_x2) {
2625
+ return _buildProposalOnServer.apply(this, arguments);
2626
+ }
2627
+ function _buildProposalOnServer() {
2628
+ _buildProposalOnServer = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee4(deps) {
2629
+ var _deps$now2, _deps$sleep2, _deps$onStep2;
2630
+ var now, sleep, step, signal, read, staged, inputs, settled, asked, since, first, proposal, outcome, _t8, _t9, _t0, _t1, _t10;
2631
+ return createForOfIteratorHelper._regenerator().w(function (_context4) {
2632
+ while (1) switch (_context4.p = _context4.n) {
2633
+ case 0:
2634
+ now = (_deps$now2 = deps.now) !== null && _deps$now2 !== void 0 ? _deps$now2 : Date.now;
2635
+ sleep = (_deps$sleep2 = deps.sleep) !== null && _deps$sleep2 !== void 0 ? _deps$sleep2 : function (ms) {
2636
+ return new Promise(function (resolve) {
2637
+ return setTimeout(resolve, ms);
2638
+ });
2639
+ };
2640
+ step = (_deps$onStep2 = deps.onStep) !== null && _deps$onStep2 !== void 0 ? _deps$onStep2 : function () {};
2641
+ signal = deps.signal;
2642
+ step({
2643
+ kind: "reading"
2644
+ });
2645
+ // The proposal says what is staged: its files are not needed here
2646
+ _context4.p = 1;
2647
+ _context4.n = 2;
2648
+ return orAborted(deps.client.get(deps.name), signal);
2649
+ case 2:
2650
+ read = _context4.v;
2651
+ _context4.n = 4;
2652
+ break;
2653
+ case 3:
2654
+ _context4.p = 3;
2655
+ _t8 = _context4.v;
2656
+ return _context4.a(2, {
2657
+ status: "failed",
2658
+ message: messageOf(_t8)
2659
+ });
2660
+ case 4:
2661
+ if (!(read === ABORTED || signal !== null && signal !== void 0 && signal.aborted)) {
2662
+ _context4.n = 5;
2663
+ break;
2664
+ }
2665
+ return _context4.a(2, abortedOutcome(signal));
2666
+ case 5:
2667
+ staged = read.staged;
2668
+ if (!(!staged || staged.inputs === null)) {
2669
+ _context4.n = 6;
2670
+ break;
2671
+ }
2672
+ return _context4.a(2, {
2673
+ status: "nothing-staged"
2674
+ });
2675
+ case 6:
2676
+ if (staged.buildRequired) {
2677
+ _context4.n = 7;
2678
+ break;
2679
+ }
2680
+ return _context4.a(2, {
2681
+ status: "already-built",
2682
+ hash: null
2683
+ });
2684
+ case 7:
2685
+ inputs = staged.inputs;
2686
+ /** Where the proposal is: served, failed, replaced, or still being built. */
2687
+ settled = function settled(proposal) {
2688
+ var _proposal$built3, _proposal$serverBuild;
2689
+ if (((_proposal$built3 = proposal.built) === null || _proposal$built3 === void 0 ? void 0 : _proposal$built3.inputs) === inputs && proposal.built.hash) {
2690
+ return {
2691
+ status: "built",
2692
+ hash: proposal.built.hash,
2693
+ proposal: proposal
2694
+ };
2695
+ }
2696
+ if (((_proposal$serverBuild = proposal.serverBuild) === null || _proposal$serverBuild === void 0 ? void 0 : _proposal$serverBuild.inputs) === inputs && proposal.serverBuild.state === "failed") {
2697
+ var _proposal$serverBuild2;
2698
+ return {
2699
+ status: "failed",
2700
+ message: (_proposal$serverBuild2 = proposal.serverBuild.error) !== null && _proposal$serverBuild2 !== void 0 ? _proposal$serverBuild2 : "Val Build could not build it."
2701
+ };
2702
+ }
2703
+ // Staged again since (by anyone): this build is not the one to wait for
2704
+ if (proposal.staged && proposal.staged.inputs !== inputs) {
2705
+ return {
2706
+ status: "failed",
2707
+ message: "The proposal's files changed while it was being built. Build it again."
2708
+ };
2709
+ }
2710
+ return null;
2711
+ };
2712
+ _context4.p = 8;
2713
+ _context4.n = 9;
2714
+ return orAborted(deps.client.askBuild(deps.name, inputs), signal);
2715
+ case 9:
2716
+ asked = _context4.v;
2717
+ _context4.n = 12;
2718
+ break;
2719
+ case 10:
2720
+ _context4.p = 10;
2721
+ _t9 = _context4.v;
2722
+ if (!serverBuildsOff(_t9)) {
2723
+ _context4.n = 11;
2724
+ break;
2725
+ }
2726
+ return _context4.a(2, {
2727
+ status: "off"
2728
+ });
2729
+ case 11:
2730
+ return _context4.a(2, {
2731
+ status: "failed",
2732
+ message: messageOf(_t9)
2733
+ });
2734
+ case 12:
2735
+ if (!(asked === ABORTED)) {
2736
+ _context4.n = 13;
2737
+ break;
2738
+ }
2739
+ return _context4.a(2, abortedOutcome(signal));
2740
+ case 13:
2741
+ // Said only now: a project Val Build does not build never was building there
2742
+ since = now();
2743
+ step({
2744
+ kind: "on-server",
2745
+ since: since
2746
+ });
2747
+ first = settled(asked);
2748
+ if (!(first !== null)) {
2749
+ _context4.n = 14;
2750
+ break;
2751
+ }
2752
+ return _context4.a(2, first);
2753
+ case 14:
2754
+ if (!(now() - since < SERVER_BUILD_WAIT_MS)) {
2755
+ _context4.n = 24;
2756
+ break;
2757
+ }
2758
+ _context4.n = 15;
2759
+ return orAborted(sleep(SERVER_BUILD_POLL_MS), signal);
2760
+ case 15:
2761
+ _t0 = _context4.v;
2762
+ _t1 = ABORTED;
2763
+ if (!(_t0 === _t1)) {
2764
+ _context4.n = 16;
2765
+ break;
2766
+ }
2767
+ return _context4.a(2, abortedOutcome(signal));
2768
+ case 16:
2769
+ proposal = void 0;
2770
+ _context4.p = 17;
2771
+ _context4.n = 18;
2772
+ return orAborted(deps.client.get(deps.name), signal);
2773
+ case 18:
2774
+ proposal = _context4.v;
2775
+ _context4.n = 21;
2776
+ break;
2777
+ case 19:
2778
+ _context4.p = 19;
2779
+ _t10 = _context4.v;
2780
+ if (gatewayGaveUp(_t10)) {
2781
+ _context4.n = 20;
2782
+ break;
2783
+ }
2784
+ return _context4.a(2, {
2785
+ status: "failed",
2786
+ message: messageOf(_t10)
2787
+ });
2788
+ case 20:
2789
+ step({
2790
+ kind: "on-server",
2791
+ since: since
2792
+ });
2793
+ return _context4.a(3, 14);
2794
+ case 21:
2795
+ if (!(proposal === ABORTED)) {
2796
+ _context4.n = 22;
2797
+ break;
2798
+ }
2799
+ return _context4.a(2, abortedOutcome(signal));
2800
+ case 22:
2801
+ // Said again, so a watchdog sees it waiting, not stalled.
2802
+ step({
2803
+ kind: "on-server",
2804
+ since: since
2805
+ });
2806
+ outcome = settled(proposal);
2807
+ if (!(outcome !== null)) {
2808
+ _context4.n = 23;
2809
+ break;
2810
+ }
2811
+ return _context4.a(2, outcome);
2812
+ case 23:
2813
+ _context4.n = 14;
2814
+ break;
2815
+ case 24:
2816
+ return _context4.a(2, {
2817
+ status: "failed",
2818
+ message: "Val Build did not finish the build in ".concat(Math.round(SERVER_BUILD_WAIT_MS / 60000), " minutes. Try again.")
2819
+ });
2820
+ }
2821
+ }, _callee4, null, [[17, 19], [8, 10], [1, 3]]);
2822
+ }));
2823
+ return _buildProposalOnServer.apply(this, arguments);
2824
+ }
2572
2825
 
2573
2826
  /*
2574
2827
  * A quick preview's files: the proposal's app bundled in the tab, for the
@@ -2782,6 +3035,9 @@ exports.BUILD_CANCELLED_MESSAGE = BUILD_CANCELLED_MESSAGE;
2782
3035
  exports.DEPLOY_STOPPED_MESSAGE = DEPLOY_STOPPED_MESSAGE;
2783
3036
  exports.MergePrepareAnswer = MergePrepareAnswer;
2784
3037
  exports.RENEW_EVERY_MS = RENEW_EVERY_MS;
3038
+ exports.SERVER_BUILDS_OFF = SERVER_BUILDS_OFF;
3039
+ exports.SERVER_BUILD_POLL_MS = SERVER_BUILD_POLL_MS;
3040
+ exports.SERVER_BUILD_WAIT_MS = SERVER_BUILD_WAIT_MS;
2785
3041
  exports.SERVE_POLL_MS = SERVE_POLL_MS;
2786
3042
  exports.SERVE_WAIT_MS = SERVE_WAIT_MS;
2787
3043
  exports.STUDIO_OUT_OF_DATE = STUDIO_OUT_OF_DATE;
@@ -2789,6 +3045,7 @@ exports.STUDIO_OUT_OF_DATE_MESSAGE = STUDIO_OUT_OF_DATE_MESSAGE;
2789
3045
  exports.StudioPublishError = StudioPublishError;
2790
3046
  exports.StudioUploadError = StudioUploadError;
2791
3047
  exports.buildProposal = buildProposal;
3048
+ exports.buildProposalOnServer = buildProposalOnServer;
2792
3049
  exports.buildQuickPreview = buildQuickPreview;
2793
3050
  exports.callJson = callJson;
2794
3051
  exports.carriedInto = carriedInto;
@@ -2309,6 +2309,8 @@ var SERVE_POLL_MS = 5000;
2309
2309
  function buildProposal(_x) {
2310
2310
  return _buildProposal.apply(this, arguments);
2311
2311
  }
2312
+
2313
+ /** What content answers, in `details.code`, for a project it does not build yet. */
2312
2314
  function _buildProposal() {
2313
2315
  _buildProposal = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee3(deps) {
2314
2316
  var _deps$now, _deps$sleep, _deps$onStep;
@@ -2569,6 +2571,257 @@ function _buildProposal() {
2569
2571
  }));
2570
2572
  return _buildProposal.apply(this, arguments);
2571
2573
  }
2574
+ var SERVER_BUILDS_OFF = "server-builds-off";
2575
+
2576
+ /** How often the proposal is read while the platform builds it. */
2577
+ var SERVER_BUILD_POLL_MS = 1000;
2578
+
2579
+ /**
2580
+ * How long a build by the platform is waited for. It is seconds
2581
+ * (valbuild/home `docs/plans/server-builds.md`); this is for the platform not
2582
+ * answering, never for a build that is merely slow.
2583
+ */
2584
+ var SERVER_BUILD_WAIT_MS = 4 * 60000;
2585
+
2586
+ /**
2587
+ * Where a build by the platform is (`buildProposalOnServer`): reading what is
2588
+ * staged, then Val Build building, rendering and serving it. Its own type,
2589
+ * not `ProposalBuildStep`'s, which every page that runs `buildProposal`
2590
+ * switches over.
2591
+ */
2592
+
2593
+ /** The calls a build by the platform makes. An error carries content's `status`. */
2594
+
2595
+ /**
2596
+ * Whether an error is content saying it does not build this project yet --
2597
+ * or not knowing the request: content from before it built anything refuses
2598
+ * a build without a publish as an invalid body (400). Either way the tab
2599
+ * builds it, as it always did.
2600
+ */
2601
+ function serverBuildsOff(error) {
2602
+ var status = statusOf(error);
2603
+ if (status === 400) return true;
2604
+ if (status !== 409) return false;
2605
+ var body = createForOfIteratorHelper._typeof(error) === "object" && error !== null && "body" in error ? error.body : null;
2606
+ var details = createForOfIteratorHelper._typeof(body) === "object" && body !== null && "details" in body ? body.details : null;
2607
+ return createForOfIteratorHelper._typeof(details) === "object" && details !== null && "code" in details && details.code === SERVER_BUILDS_OFF;
2608
+ }
2609
+
2610
+ /**
2611
+ * Have the platform build the proposal's staged files and serve the build at
2612
+ * its address (valbuild/home `docs/plans/server-builds.md`): nothing is built
2613
+ * in this tab, so no bundler is loaded, no builder tab is opened, and a tab
2614
+ * that closes meanwhile loses nothing.
2615
+ *
2616
+ * Content answers at once and builds in a job; the proposal is read until it
2617
+ * says the build of these inputs is served, or why it is not. `off` when
2618
+ * content does not build this project yet: `buildProposal` builds it in the
2619
+ * tab, as before.
2620
+ *
2621
+ * A cancel stops the waiting, not the build: the platform finishes it, and
2622
+ * the proposal's address serves it when it renders.
2623
+ */
2624
+ function buildProposalOnServer(_x2) {
2625
+ return _buildProposalOnServer.apply(this, arguments);
2626
+ }
2627
+ function _buildProposalOnServer() {
2628
+ _buildProposalOnServer = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee4(deps) {
2629
+ var _deps$now2, _deps$sleep2, _deps$onStep2;
2630
+ var now, sleep, step, signal, read, staged, inputs, settled, asked, since, first, proposal, outcome, _t8, _t9, _t0, _t1, _t10;
2631
+ return createForOfIteratorHelper._regenerator().w(function (_context4) {
2632
+ while (1) switch (_context4.p = _context4.n) {
2633
+ case 0:
2634
+ now = (_deps$now2 = deps.now) !== null && _deps$now2 !== void 0 ? _deps$now2 : Date.now;
2635
+ sleep = (_deps$sleep2 = deps.sleep) !== null && _deps$sleep2 !== void 0 ? _deps$sleep2 : function (ms) {
2636
+ return new Promise(function (resolve) {
2637
+ return setTimeout(resolve, ms);
2638
+ });
2639
+ };
2640
+ step = (_deps$onStep2 = deps.onStep) !== null && _deps$onStep2 !== void 0 ? _deps$onStep2 : function () {};
2641
+ signal = deps.signal;
2642
+ step({
2643
+ kind: "reading"
2644
+ });
2645
+ // The proposal says what is staged: its files are not needed here
2646
+ _context4.p = 1;
2647
+ _context4.n = 2;
2648
+ return orAborted(deps.client.get(deps.name), signal);
2649
+ case 2:
2650
+ read = _context4.v;
2651
+ _context4.n = 4;
2652
+ break;
2653
+ case 3:
2654
+ _context4.p = 3;
2655
+ _t8 = _context4.v;
2656
+ return _context4.a(2, {
2657
+ status: "failed",
2658
+ message: messageOf(_t8)
2659
+ });
2660
+ case 4:
2661
+ if (!(read === ABORTED || signal !== null && signal !== void 0 && signal.aborted)) {
2662
+ _context4.n = 5;
2663
+ break;
2664
+ }
2665
+ return _context4.a(2, abortedOutcome(signal));
2666
+ case 5:
2667
+ staged = read.staged;
2668
+ if (!(!staged || staged.inputs === null)) {
2669
+ _context4.n = 6;
2670
+ break;
2671
+ }
2672
+ return _context4.a(2, {
2673
+ status: "nothing-staged"
2674
+ });
2675
+ case 6:
2676
+ if (staged.buildRequired) {
2677
+ _context4.n = 7;
2678
+ break;
2679
+ }
2680
+ return _context4.a(2, {
2681
+ status: "already-built",
2682
+ hash: null
2683
+ });
2684
+ case 7:
2685
+ inputs = staged.inputs;
2686
+ /** Where the proposal is: served, failed, replaced, or still being built. */
2687
+ settled = function settled(proposal) {
2688
+ var _proposal$built3, _proposal$serverBuild;
2689
+ if (((_proposal$built3 = proposal.built) === null || _proposal$built3 === void 0 ? void 0 : _proposal$built3.inputs) === inputs && proposal.built.hash) {
2690
+ return {
2691
+ status: "built",
2692
+ hash: proposal.built.hash,
2693
+ proposal: proposal
2694
+ };
2695
+ }
2696
+ if (((_proposal$serverBuild = proposal.serverBuild) === null || _proposal$serverBuild === void 0 ? void 0 : _proposal$serverBuild.inputs) === inputs && proposal.serverBuild.state === "failed") {
2697
+ var _proposal$serverBuild2;
2698
+ return {
2699
+ status: "failed",
2700
+ message: (_proposal$serverBuild2 = proposal.serverBuild.error) !== null && _proposal$serverBuild2 !== void 0 ? _proposal$serverBuild2 : "Val Build could not build it."
2701
+ };
2702
+ }
2703
+ // Staged again since (by anyone): this build is not the one to wait for
2704
+ if (proposal.staged && proposal.staged.inputs !== inputs) {
2705
+ return {
2706
+ status: "failed",
2707
+ message: "The proposal's files changed while it was being built. Build it again."
2708
+ };
2709
+ }
2710
+ return null;
2711
+ };
2712
+ _context4.p = 8;
2713
+ _context4.n = 9;
2714
+ return orAborted(deps.client.askBuild(deps.name, inputs), signal);
2715
+ case 9:
2716
+ asked = _context4.v;
2717
+ _context4.n = 12;
2718
+ break;
2719
+ case 10:
2720
+ _context4.p = 10;
2721
+ _t9 = _context4.v;
2722
+ if (!serverBuildsOff(_t9)) {
2723
+ _context4.n = 11;
2724
+ break;
2725
+ }
2726
+ return _context4.a(2, {
2727
+ status: "off"
2728
+ });
2729
+ case 11:
2730
+ return _context4.a(2, {
2731
+ status: "failed",
2732
+ message: messageOf(_t9)
2733
+ });
2734
+ case 12:
2735
+ if (!(asked === ABORTED)) {
2736
+ _context4.n = 13;
2737
+ break;
2738
+ }
2739
+ return _context4.a(2, abortedOutcome(signal));
2740
+ case 13:
2741
+ // Said only now: a project Val Build does not build never was building there
2742
+ since = now();
2743
+ step({
2744
+ kind: "on-server",
2745
+ since: since
2746
+ });
2747
+ first = settled(asked);
2748
+ if (!(first !== null)) {
2749
+ _context4.n = 14;
2750
+ break;
2751
+ }
2752
+ return _context4.a(2, first);
2753
+ case 14:
2754
+ if (!(now() - since < SERVER_BUILD_WAIT_MS)) {
2755
+ _context4.n = 24;
2756
+ break;
2757
+ }
2758
+ _context4.n = 15;
2759
+ return orAborted(sleep(SERVER_BUILD_POLL_MS), signal);
2760
+ case 15:
2761
+ _t0 = _context4.v;
2762
+ _t1 = ABORTED;
2763
+ if (!(_t0 === _t1)) {
2764
+ _context4.n = 16;
2765
+ break;
2766
+ }
2767
+ return _context4.a(2, abortedOutcome(signal));
2768
+ case 16:
2769
+ proposal = void 0;
2770
+ _context4.p = 17;
2771
+ _context4.n = 18;
2772
+ return orAborted(deps.client.get(deps.name), signal);
2773
+ case 18:
2774
+ proposal = _context4.v;
2775
+ _context4.n = 21;
2776
+ break;
2777
+ case 19:
2778
+ _context4.p = 19;
2779
+ _t10 = _context4.v;
2780
+ if (gatewayGaveUp(_t10)) {
2781
+ _context4.n = 20;
2782
+ break;
2783
+ }
2784
+ return _context4.a(2, {
2785
+ status: "failed",
2786
+ message: messageOf(_t10)
2787
+ });
2788
+ case 20:
2789
+ step({
2790
+ kind: "on-server",
2791
+ since: since
2792
+ });
2793
+ return _context4.a(3, 14);
2794
+ case 21:
2795
+ if (!(proposal === ABORTED)) {
2796
+ _context4.n = 22;
2797
+ break;
2798
+ }
2799
+ return _context4.a(2, abortedOutcome(signal));
2800
+ case 22:
2801
+ // Said again, so a watchdog sees it waiting, not stalled.
2802
+ step({
2803
+ kind: "on-server",
2804
+ since: since
2805
+ });
2806
+ outcome = settled(proposal);
2807
+ if (!(outcome !== null)) {
2808
+ _context4.n = 23;
2809
+ break;
2810
+ }
2811
+ return _context4.a(2, outcome);
2812
+ case 23:
2813
+ _context4.n = 14;
2814
+ break;
2815
+ case 24:
2816
+ return _context4.a(2, {
2817
+ status: "failed",
2818
+ message: "Val Build did not finish the build in ".concat(Math.round(SERVER_BUILD_WAIT_MS / 60000), " minutes. Try again.")
2819
+ });
2820
+ }
2821
+ }, _callee4, null, [[17, 19], [8, 10], [1, 3]]);
2822
+ }));
2823
+ return _buildProposalOnServer.apply(this, arguments);
2824
+ }
2572
2825
 
2573
2826
  /*
2574
2827
  * A quick preview's files: the proposal's app bundled in the tab, for the
@@ -2782,6 +3035,9 @@ exports.BUILD_CANCELLED_MESSAGE = BUILD_CANCELLED_MESSAGE;
2782
3035
  exports.DEPLOY_STOPPED_MESSAGE = DEPLOY_STOPPED_MESSAGE;
2783
3036
  exports.MergePrepareAnswer = MergePrepareAnswer;
2784
3037
  exports.RENEW_EVERY_MS = RENEW_EVERY_MS;
3038
+ exports.SERVER_BUILDS_OFF = SERVER_BUILDS_OFF;
3039
+ exports.SERVER_BUILD_POLL_MS = SERVER_BUILD_POLL_MS;
3040
+ exports.SERVER_BUILD_WAIT_MS = SERVER_BUILD_WAIT_MS;
2785
3041
  exports.SERVE_POLL_MS = SERVE_POLL_MS;
2786
3042
  exports.SERVE_WAIT_MS = SERVE_WAIT_MS;
2787
3043
  exports.STUDIO_OUT_OF_DATE = STUDIO_OUT_OF_DATE;
@@ -2789,6 +3045,7 @@ exports.STUDIO_OUT_OF_DATE_MESSAGE = STUDIO_OUT_OF_DATE_MESSAGE;
2789
3045
  exports.StudioPublishError = StudioPublishError;
2790
3046
  exports.StudioUploadError = StudioUploadError;
2791
3047
  exports.buildProposal = buildProposal;
3048
+ exports.buildProposalOnServer = buildProposalOnServer;
2792
3049
  exports.buildQuickPreview = buildQuickPreview;
2793
3050
  exports.callJson = callJson;
2794
3051
  exports.carriedInto = carriedInto;
@@ -2306,6 +2306,8 @@ var SERVE_POLL_MS = 5000;
2306
2306
  function buildProposal(_x) {
2307
2307
  return _buildProposal.apply(this, arguments);
2308
2308
  }
2309
+
2310
+ /** What content answers, in `details.code`, for a project it does not build yet. */
2309
2311
  function _buildProposal() {
2310
2312
  _buildProposal = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(deps) {
2311
2313
  var _deps$now, _deps$sleep, _deps$onStep;
@@ -2566,6 +2568,257 @@ function _buildProposal() {
2566
2568
  }));
2567
2569
  return _buildProposal.apply(this, arguments);
2568
2570
  }
2571
+ var SERVER_BUILDS_OFF = "server-builds-off";
2572
+
2573
+ /** How often the proposal is read while the platform builds it. */
2574
+ var SERVER_BUILD_POLL_MS = 1000;
2575
+
2576
+ /**
2577
+ * How long a build by the platform is waited for. It is seconds
2578
+ * (valbuild/home `docs/plans/server-builds.md`); this is for the platform not
2579
+ * answering, never for a build that is merely slow.
2580
+ */
2581
+ var SERVER_BUILD_WAIT_MS = 4 * 60000;
2582
+
2583
+ /**
2584
+ * Where a build by the platform is (`buildProposalOnServer`): reading what is
2585
+ * staged, then Val Build building, rendering and serving it. Its own type,
2586
+ * not `ProposalBuildStep`'s, which every page that runs `buildProposal`
2587
+ * switches over.
2588
+ */
2589
+
2590
+ /** The calls a build by the platform makes. An error carries content's `status`. */
2591
+
2592
+ /**
2593
+ * Whether an error is content saying it does not build this project yet --
2594
+ * or not knowing the request: content from before it built anything refuses
2595
+ * a build without a publish as an invalid body (400). Either way the tab
2596
+ * builds it, as it always did.
2597
+ */
2598
+ function serverBuildsOff(error) {
2599
+ var status = statusOf(error);
2600
+ if (status === 400) return true;
2601
+ if (status !== 409) return false;
2602
+ var body = _typeof(error) === "object" && error !== null && "body" in error ? error.body : null;
2603
+ var details = _typeof(body) === "object" && body !== null && "details" in body ? body.details : null;
2604
+ return _typeof(details) === "object" && details !== null && "code" in details && details.code === SERVER_BUILDS_OFF;
2605
+ }
2606
+
2607
+ /**
2608
+ * Have the platform build the proposal's staged files and serve the build at
2609
+ * its address (valbuild/home `docs/plans/server-builds.md`): nothing is built
2610
+ * in this tab, so no bundler is loaded, no builder tab is opened, and a tab
2611
+ * that closes meanwhile loses nothing.
2612
+ *
2613
+ * Content answers at once and builds in a job; the proposal is read until it
2614
+ * says the build of these inputs is served, or why it is not. `off` when
2615
+ * content does not build this project yet: `buildProposal` builds it in the
2616
+ * tab, as before.
2617
+ *
2618
+ * A cancel stops the waiting, not the build: the platform finishes it, and
2619
+ * the proposal's address serves it when it renders.
2620
+ */
2621
+ function buildProposalOnServer(_x2) {
2622
+ return _buildProposalOnServer.apply(this, arguments);
2623
+ }
2624
+ function _buildProposalOnServer() {
2625
+ _buildProposalOnServer = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(deps) {
2626
+ var _deps$now2, _deps$sleep2, _deps$onStep2;
2627
+ var now, sleep, step, signal, read, staged, inputs, settled, asked, since, first, proposal, outcome, _t8, _t9, _t0, _t1, _t10;
2628
+ return _regenerator().w(function (_context4) {
2629
+ while (1) switch (_context4.p = _context4.n) {
2630
+ case 0:
2631
+ now = (_deps$now2 = deps.now) !== null && _deps$now2 !== void 0 ? _deps$now2 : Date.now;
2632
+ sleep = (_deps$sleep2 = deps.sleep) !== null && _deps$sleep2 !== void 0 ? _deps$sleep2 : function (ms) {
2633
+ return new Promise(function (resolve) {
2634
+ return setTimeout(resolve, ms);
2635
+ });
2636
+ };
2637
+ step = (_deps$onStep2 = deps.onStep) !== null && _deps$onStep2 !== void 0 ? _deps$onStep2 : function () {};
2638
+ signal = deps.signal;
2639
+ step({
2640
+ kind: "reading"
2641
+ });
2642
+ // The proposal says what is staged: its files are not needed here
2643
+ _context4.p = 1;
2644
+ _context4.n = 2;
2645
+ return orAborted(deps.client.get(deps.name), signal);
2646
+ case 2:
2647
+ read = _context4.v;
2648
+ _context4.n = 4;
2649
+ break;
2650
+ case 3:
2651
+ _context4.p = 3;
2652
+ _t8 = _context4.v;
2653
+ return _context4.a(2, {
2654
+ status: "failed",
2655
+ message: messageOf(_t8)
2656
+ });
2657
+ case 4:
2658
+ if (!(read === ABORTED || signal !== null && signal !== void 0 && signal.aborted)) {
2659
+ _context4.n = 5;
2660
+ break;
2661
+ }
2662
+ return _context4.a(2, abortedOutcome(signal));
2663
+ case 5:
2664
+ staged = read.staged;
2665
+ if (!(!staged || staged.inputs === null)) {
2666
+ _context4.n = 6;
2667
+ break;
2668
+ }
2669
+ return _context4.a(2, {
2670
+ status: "nothing-staged"
2671
+ });
2672
+ case 6:
2673
+ if (staged.buildRequired) {
2674
+ _context4.n = 7;
2675
+ break;
2676
+ }
2677
+ return _context4.a(2, {
2678
+ status: "already-built",
2679
+ hash: null
2680
+ });
2681
+ case 7:
2682
+ inputs = staged.inputs;
2683
+ /** Where the proposal is: served, failed, replaced, or still being built. */
2684
+ settled = function settled(proposal) {
2685
+ var _proposal$built3, _proposal$serverBuild;
2686
+ if (((_proposal$built3 = proposal.built) === null || _proposal$built3 === void 0 ? void 0 : _proposal$built3.inputs) === inputs && proposal.built.hash) {
2687
+ return {
2688
+ status: "built",
2689
+ hash: proposal.built.hash,
2690
+ proposal: proposal
2691
+ };
2692
+ }
2693
+ if (((_proposal$serverBuild = proposal.serverBuild) === null || _proposal$serverBuild === void 0 ? void 0 : _proposal$serverBuild.inputs) === inputs && proposal.serverBuild.state === "failed") {
2694
+ var _proposal$serverBuild2;
2695
+ return {
2696
+ status: "failed",
2697
+ message: (_proposal$serverBuild2 = proposal.serverBuild.error) !== null && _proposal$serverBuild2 !== void 0 ? _proposal$serverBuild2 : "Val Build could not build it."
2698
+ };
2699
+ }
2700
+ // Staged again since (by anyone): this build is not the one to wait for
2701
+ if (proposal.staged && proposal.staged.inputs !== inputs) {
2702
+ return {
2703
+ status: "failed",
2704
+ message: "The proposal's files changed while it was being built. Build it again."
2705
+ };
2706
+ }
2707
+ return null;
2708
+ };
2709
+ _context4.p = 8;
2710
+ _context4.n = 9;
2711
+ return orAborted(deps.client.askBuild(deps.name, inputs), signal);
2712
+ case 9:
2713
+ asked = _context4.v;
2714
+ _context4.n = 12;
2715
+ break;
2716
+ case 10:
2717
+ _context4.p = 10;
2718
+ _t9 = _context4.v;
2719
+ if (!serverBuildsOff(_t9)) {
2720
+ _context4.n = 11;
2721
+ break;
2722
+ }
2723
+ return _context4.a(2, {
2724
+ status: "off"
2725
+ });
2726
+ case 11:
2727
+ return _context4.a(2, {
2728
+ status: "failed",
2729
+ message: messageOf(_t9)
2730
+ });
2731
+ case 12:
2732
+ if (!(asked === ABORTED)) {
2733
+ _context4.n = 13;
2734
+ break;
2735
+ }
2736
+ return _context4.a(2, abortedOutcome(signal));
2737
+ case 13:
2738
+ // Said only now: a project Val Build does not build never was building there
2739
+ since = now();
2740
+ step({
2741
+ kind: "on-server",
2742
+ since: since
2743
+ });
2744
+ first = settled(asked);
2745
+ if (!(first !== null)) {
2746
+ _context4.n = 14;
2747
+ break;
2748
+ }
2749
+ return _context4.a(2, first);
2750
+ case 14:
2751
+ if (!(now() - since < SERVER_BUILD_WAIT_MS)) {
2752
+ _context4.n = 24;
2753
+ break;
2754
+ }
2755
+ _context4.n = 15;
2756
+ return orAborted(sleep(SERVER_BUILD_POLL_MS), signal);
2757
+ case 15:
2758
+ _t0 = _context4.v;
2759
+ _t1 = ABORTED;
2760
+ if (!(_t0 === _t1)) {
2761
+ _context4.n = 16;
2762
+ break;
2763
+ }
2764
+ return _context4.a(2, abortedOutcome(signal));
2765
+ case 16:
2766
+ proposal = void 0;
2767
+ _context4.p = 17;
2768
+ _context4.n = 18;
2769
+ return orAborted(deps.client.get(deps.name), signal);
2770
+ case 18:
2771
+ proposal = _context4.v;
2772
+ _context4.n = 21;
2773
+ break;
2774
+ case 19:
2775
+ _context4.p = 19;
2776
+ _t10 = _context4.v;
2777
+ if (gatewayGaveUp(_t10)) {
2778
+ _context4.n = 20;
2779
+ break;
2780
+ }
2781
+ return _context4.a(2, {
2782
+ status: "failed",
2783
+ message: messageOf(_t10)
2784
+ });
2785
+ case 20:
2786
+ step({
2787
+ kind: "on-server",
2788
+ since: since
2789
+ });
2790
+ return _context4.a(3, 14);
2791
+ case 21:
2792
+ if (!(proposal === ABORTED)) {
2793
+ _context4.n = 22;
2794
+ break;
2795
+ }
2796
+ return _context4.a(2, abortedOutcome(signal));
2797
+ case 22:
2798
+ // Said again, so a watchdog sees it waiting, not stalled.
2799
+ step({
2800
+ kind: "on-server",
2801
+ since: since
2802
+ });
2803
+ outcome = settled(proposal);
2804
+ if (!(outcome !== null)) {
2805
+ _context4.n = 23;
2806
+ break;
2807
+ }
2808
+ return _context4.a(2, outcome);
2809
+ case 23:
2810
+ _context4.n = 14;
2811
+ break;
2812
+ case 24:
2813
+ return _context4.a(2, {
2814
+ status: "failed",
2815
+ message: "Val Build did not finish the build in ".concat(Math.round(SERVER_BUILD_WAIT_MS / 60000), " minutes. Try again.")
2816
+ });
2817
+ }
2818
+ }, _callee4, null, [[17, 19], [8, 10], [1, 3]]);
2819
+ }));
2820
+ return _buildProposalOnServer.apply(this, arguments);
2821
+ }
2569
2822
 
2570
2823
  /*
2571
2824
  * A quick preview's files: the proposal's app bundled in the tab, for the
@@ -2774,4 +3027,4 @@ function contentTypeOf(name) {
2774
3027
  return (_CONTENT_TYPES$extens = CONTENT_TYPES[extension]) !== null && _CONTENT_TYPES$extens !== void 0 ? _CONTENT_TYPES$extens : "application/octet-stream";
2775
3028
  }
2776
3029
 
2777
- export { BUILD_CANCELLED_MESSAGE, DEPLOY_STOPPED_MESSAGE, MergePrepareAnswer, RENEW_EVERY_MS, SERVE_POLL_MS, SERVE_WAIT_MS, STUDIO_OUT_OF_DATE, STUDIO_OUT_OF_DATE_MESSAGE, StudioPublishError, StudioUploadError, buildProposal, buildQuickPreview, callJson, carriedInto, clientFilesOf, createStudioJobClient, createStudioPublishClient, isExpiredSlot, isMergeChange, isTransientPublishError, layerMoved, loadedLayer, markStudioOutOfDate, onPublishRefusal, publishApiOf, readLiveLayer, rememberLoadedLayer, resetLoadedLayerForTests, runJobToEnd, runStudioDeploy, runStudioJob, runStudioPublish, siteMovedSinceLoad, splitBinaryFiles, studioOutOfDateRefusals, subscribeStudioOutOfDate, withCommittedFiles, withoutLockfiles };
3030
+ export { BUILD_CANCELLED_MESSAGE, DEPLOY_STOPPED_MESSAGE, MergePrepareAnswer, RENEW_EVERY_MS, SERVER_BUILDS_OFF, SERVER_BUILD_POLL_MS, SERVER_BUILD_WAIT_MS, SERVE_POLL_MS, SERVE_WAIT_MS, STUDIO_OUT_OF_DATE, STUDIO_OUT_OF_DATE_MESSAGE, StudioPublishError, StudioUploadError, buildProposal, buildProposalOnServer, buildQuickPreview, callJson, carriedInto, clientFilesOf, createStudioJobClient, createStudioPublishClient, isExpiredSlot, isMergeChange, isTransientPublishError, layerMoved, loadedLayer, markStudioOutOfDate, onPublishRefusal, publishApiOf, readLiveLayer, rememberLoadedLayer, resetLoadedLayerForTests, runJobToEnd, runStudioDeploy, runStudioJob, runStudioPublish, siteMovedSinceLoad, splitBinaryFiles, studioOutOfDateRefusals, subscribeStudioOutOfDate, withCommittedFiles, withoutLockfiles };