@valbuild/shared 0.150.20 → 0.151.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +236 -0
  2. package/client/dist/valbuild-shared-client.cjs.dev.js +1 -1
  3. package/client/dist/valbuild-shared-client.cjs.prod.js +1 -1
  4. package/client/dist/valbuild-shared-client.esm.js +1 -1
  5. package/dist/declarations/src/internal/ApiRoutes.d.ts +253 -2
  6. package/dist/declarations/src/internal/content/applyChain.d.ts +124 -0
  7. package/dist/declarations/src/internal/content/buildRegistry.d.ts +36 -0
  8. package/dist/declarations/src/internal/content/jsonValuesOps.d.ts +110 -0
  9. package/dist/declarations/src/internal/content/moduleStore.d.ts +119 -0
  10. package/dist/declarations/src/internal/content/source.d.ts +71 -0
  11. package/dist/declarations/src/internal/content/version.d.ts +151 -0
  12. package/dist/declarations/src/internal/index.d.ts +8 -1
  13. package/dist/declarations/src/internal/publish/projectSource.d.ts +28 -0
  14. package/dist/declarations/src/internal/search/searchIndex.d.ts +34 -3
  15. package/dist/declarations/src/internal/server/types.d.ts +9 -1
  16. package/dist/declarations/src/internal/valCanvasProtocol.d.ts +46 -0
  17. package/dist/declarations/src/internal/valClientHeader.d.ts +36 -0
  18. package/dist/declarations/src/publish/callJson.d.ts +15 -0
  19. package/dist/declarations/src/publish/jobClient.d.ts +7 -1
  20. package/dist/declarations/src/publish/publishClient.d.ts +2 -14
  21. package/dist/{getSitemapTree-fef55872.esm.js → getSitemapTree-3c1b9e57.esm.js} +18 -2
  22. package/dist/{getSitemapTree-6dfa2f4e.cjs.dev.js → getSitemapTree-59c5ed5d.cjs.dev.js} +18 -2
  23. package/dist/{getSitemapTree-8a04f3cf.cjs.prod.js → getSitemapTree-6b9024d1.cjs.prod.js} +18 -2
  24. package/dist/{protocol-d0efb8cd.cjs.dev.js → projectSource-2a58ef15.cjs.dev.js} +221 -0
  25. package/dist/{protocol-55cfa3cf.esm.js → projectSource-7f0ee83c.esm.js} +216 -1
  26. package/dist/{protocol-b1055ef0.cjs.prod.js → projectSource-ed6aec39.cjs.prod.js} +221 -0
  27. package/internal/dist/valbuild-shared-internal.cjs.dev.js +2387 -189
  28. package/internal/dist/valbuild-shared-internal.cjs.prod.js +2387 -189
  29. package/internal/dist/valbuild-shared-internal.esm.js +2332 -172
  30. package/package.json +3 -3
  31. package/publish/dist/valbuild-shared-publish.cjs.dev.js +48 -155
  32. package/publish/dist/valbuild-shared-publish.cjs.prod.js +48 -155
  33. package/publish/dist/valbuild-shared-publish.esm.js +17 -123
  34. package/dist/declarations/src/internal/server/requestScopedMemo.d.ts +0 -49
@@ -57,6 +57,28 @@ export type SearchIndex = {
57
57
  * re-indexing one module exists to avoid.
58
58
  */
59
59
  docsByModule: Map<ModuleFilePath, Set<string>>;
60
+ /**
61
+ * For a module whose root is a record: each entry's documents, and the
62
+ * value they were made from, by key -- so indexing it again walks only the
63
+ * entries whose value is a different object (see {@link indexModule}).
64
+ */
65
+ /**
66
+ * What each module was last indexed from, whole: the same source object
67
+ * under a schema that says the same is not walked again. Removing a
68
+ * document from a big index is slow (FlexSearch looks for it everywhere),
69
+ * so indexing an unchanged module again was the cost of a pass.
70
+ */
71
+ walkedByModule: Map<ModuleFilePath, {
72
+ source: Json;
73
+ schema: string;
74
+ }>;
75
+ entriesByModule: Map<ModuleFilePath, {
76
+ schema: SerializedSchema;
77
+ entries: Map<string, {
78
+ source: Json;
79
+ docs: string[];
80
+ }>;
81
+ }>;
60
82
  };
61
83
  /** An index with nothing in it. Fill it with {@link indexModule}. */
62
84
  export declare function createSearchIndex(): SearchIndex;
@@ -70,10 +92,19 @@ export declare function createSearchIndex(): SearchIndex;
70
92
  * `.jsonValues()` entries are indexed only once their content is loaded — an
71
93
  * un-loaded entry is an opaque marker that {@link traverseSchemaSource} skips, so
72
94
  * the index is naturally PARTIAL and grows as batches land. That is why the search
73
- * UI loads the entries on the first query and re-indexes (debounced) as they
74
- * arrive, rather than pretending an empty result set is an answer.
95
+ * UI loads the entries on the first query and re-indexes as they arrive, rather
96
+ * than pretending an empty result set is an answer -- and why a module whose
97
+ * root is a record is re-indexed by entry ({@link indexRecordModule}): a pass
98
+ * per batch walks the batch, not the record.
99
+ */
100
+ export declare function indexModule(searchIndex: SearchIndex, moduleFilePath: ModuleFilePath, source: Json, schema: SerializedSchema,
101
+ /**
102
+ * Stop by this time (`performance.now()`), for a caller that must not hold
103
+ * the thread: answers `false` when it stopped with entries of a record
104
+ * module still to walk, and the same call again goes on from there. A
105
+ * module that is not a record is walked whole, whatever the time.
75
106
  */
76
- export declare function indexModule(searchIndex: SearchIndex, moduleFilePath: ModuleFilePath, source: Json, schema: SerializedSchema): void;
107
+ until?: number): boolean;
77
108
  /**
78
109
  * Drop everything indexed for one module.
79
110
  *
@@ -45,7 +45,15 @@ export type ValServerJsonResult<Body extends Json | never = never, Error extends
45
45
  status: 400;
46
46
  json: Error;
47
47
  } : never);
48
- export type ValServerGenericResult = ValServerJsonResult<Json> | ValServerError | ValServerRedirectResult<ValCookiesNames> | ValServerResult<ValCookiesNames, string | ReadableStream<Uint8Array>>;
48
+ /**
49
+ * Nothing moved since the ETag the caller sent back: no body, as HTTP
50
+ * requires of a 304.
51
+ */
52
+ export type ValServerNotModified = {
53
+ status: 304;
54
+ headers?: Record<string, string | undefined>;
55
+ };
56
+ export type ValServerGenericResult = ValServerNotModified | ValServerJsonResult<Json> | ValServerError | ValServerRedirectResult<ValCookiesNames> | ValServerResult<ValCookiesNames, string | ReadableStream<Uint8Array>>;
49
57
  export type ValUIRequestHandler = (path: string, url: string) => Promise<ValServerGenericResult>;
50
58
  export type ValSession = {
51
59
  mode: "unauthorized";
@@ -93,6 +93,26 @@ export type ValCanvasPageMessage = {
93
93
  draftMode: boolean;
94
94
  /** The URL the frame ended up on, which a redirect may have changed. */
95
95
  url: string;
96
+ /**
97
+ * The modules the page holds a draft of: what its server read for this
98
+ * request (`<ValProvider draft>`). The studio sends each of them in its
99
+ * catch-up, pending or not, so a draft that stopped being the draft
100
+ * before the studio was there to say so -- discarded, published -- is
101
+ * replaced rather than kept. Absent from a page of the release before,
102
+ * and from a page that read no draft.
103
+ */
104
+ holds?: ModuleFilePath[];
105
+ /**
106
+ * The version the page holds each of {@link holds} at, where its draft
107
+ * says (`draftHoldsVersions`): the studio does not send one it would
108
+ * send at the same version -- the same content. Absent from a page of
109
+ * the release before, which is sent everything it holds, as before.
110
+ */
111
+ holdsVersions?: Partial<Record<ModuleFilePath, {
112
+ schema: string;
113
+ base: string;
114
+ chain: string;
115
+ }>>;
96
116
  } | {
97
117
  val: typeof VAL_CANVAS_MESSAGE;
98
118
  type: "elements";
@@ -257,8 +277,18 @@ export type ValCanvasStudioMessage = {
257
277
  /**
258
278
  * The whole module source. `unknown` because the page's own types are the
259
279
  * ones that give it meaning; nothing in between should be interpreting it.
280
+ *
281
+ * @deprecated A page of this release reads `read`; this is for a page
282
+ * of the release before. Goes in the release after the one that ships
283
+ * `read`, with the bridges' fallback that reads it: see
284
+ * "Release-delayed deletions" in `docs/plans/one-content-model.md`.
260
285
  */
261
286
  source: unknown;
287
+ /**
288
+ * The same module as a read (`ModuleRead`), which the page keeps as it
289
+ * is. Optional: a Studio from the release before sends `source` alone.
290
+ */
291
+ read?: unknown;
262
292
  } | {
263
293
  /**
264
294
  * Everything the studio holds has been sent.
@@ -276,6 +306,22 @@ export type ValCanvasStudioMessage = {
276
306
  */
277
307
  val: typeof VAL_CANVAS_MESSAGE;
278
308
  type: "sourcesSynced";
309
+ } | {
310
+ /**
311
+ * The server holds the editor's changes up to this version (`seq`, the
312
+ * server's `ProjectVersion.seq`; `null` from a server that reports
313
+ * none). Sent once after `sourcesSynced`, then every time it moves.
314
+ *
315
+ * What the page re-reads its server-rendered content on (a loader, a
316
+ * server component): those only show an edit once the server has it,
317
+ * so a refresh sent when the edit was typed raced the write and came
318
+ * back without it -- which a ten second safety refresh used to catch.
319
+ * A bridge of the release before ignores it, as it ignores any type it
320
+ * does not know.
321
+ */
322
+ val: typeof VAL_CANVAS_MESSAGE;
323
+ type: "confirmed";
324
+ seq: number | null;
279
325
  } | {
280
326
  /**
281
327
  * What colour to outline the page's content in.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Which Val package, at which version, sent a request to Val Build.
3
+ *
4
+ * Sent on every request a Val package makes to Val's own services from a
5
+ * server or a CLI -- the content host, `/api/login`, the analytics ingest --
6
+ * so Val Build can tell which versions are still out there and which old
7
+ * endpoints they still call. Without it an endpoint can never be retired with
8
+ * any confidence: a request from a site nobody has upgraded in a year looks
9
+ * exactly like one from yesterday's release.
10
+ *
11
+ * The value is a product token, `@valbuild/server@0.152.0`: the package whose
12
+ * code built the request, because that is what decides which endpoints are
13
+ * called. Not the framework binding or the app: those do not choose URLs.
14
+ *
15
+ * NOT sent from the browser. A custom header on a cross-origin request makes
16
+ * the browser send a CORS preflight, and a host that does not list the header
17
+ * in `Access-Control-Allow-Headers` refuses the request outright -- so adding
18
+ * it there is a change to every such host first.
19
+ *
20
+ * It identifies the code, not the caller: anyone can send any value, so it is
21
+ * for counting, never for deciding what a request may do.
22
+ */
23
+ export declare const VAL_CLIENT_HEADER = "x-val-client";
24
+ /** Packages that send {@link VAL_CLIENT_HEADER}. */
25
+ export type ValClientPackage = "@valbuild/server" | "@valbuild/cli";
26
+ /**
27
+ * `{ "x-val-client": "@valbuild/server@0.152.0" }`, to spread into a
28
+ * request's headers.
29
+ *
30
+ * A version that could not be read is sent as `unknown` rather than leaving
31
+ * the header out: "a server too broken to know its version" is still a thing
32
+ * worth counting, and it is not the same as "a version older than the header".
33
+ */
34
+ export declare function valClientHeaders(pkg: ValClientPackage, version: string | null | undefined): {
35
+ [VAL_CLIENT_HEADER]: string;
36
+ };
@@ -19,3 +19,18 @@ export declare function isTransientPublishError(error: unknown): boolean;
19
19
  * `StudioPublishError` carrying the answer's own message.
20
20
  */
21
21
  export declare function callJson(fetchImpl: typeof fetch, url: string, method: "GET" | "POST", body?: unknown): Promise<unknown>;
22
+ /**
23
+ * One JSON READ through this origin, BY VERSION: `held` is the answer the
24
+ * caller holds, with its `ETag`, which goes as `If-None-Match`. While the
25
+ * answer is still that one (a 304, with no body) it is `held` again;
26
+ * otherwise the new body, `parse`d, with its `ETag` -- `null` when the answer
27
+ * named none, so there is nothing to hold it by. Errors as {@link callJson}
28
+ * throws them.
29
+ */
30
+ export declare function readJsonByVersion<T>(fetchImpl: typeof fetch, url: string, held: {
31
+ etag: string;
32
+ value: T;
33
+ } | null, parse: (body: unknown) => T): Promise<{
34
+ etag: string | null;
35
+ value: T;
36
+ }>;
@@ -24,7 +24,13 @@ export type StudioJobClient = {
24
24
  * failed: `types`, an upgrade's new type errors (Publish anyway on home's
25
25
  * upgrade page). Content refuses it over anything else.
26
26
  */
27
- anyway?: "types"[]): Promise<PressResponse>;
27
+ anyway?: "types"[],
28
+ /**
29
+ * The message of the commit Publish makes first, in a project connected
30
+ * to git (the proposal's branch, before it merges): from the project's
31
+ * `studio.commitMessage` setting. Absent: content's default.
32
+ */
33
+ commitMessage?: string): Promise<PressResponse>;
28
34
  /** Try again: resume a paused queue and press anew. */
29
35
  tryAgain(requestId: string, tab: string): Promise<PressResponse>;
30
36
  /** Where a press is. */
@@ -22,8 +22,9 @@
22
22
  * own authorisation and a cookie sent to a storage host is a cookie leaked to
23
23
  * one.
24
24
  */
25
- import { ArtifactsResponse, BuildTargetResponse, DeclareBody, DeclareResponse, PromoteResponse, StatusResponse, UploadSlot, VerifyResponse } from "../internal/index.js";
25
+ import { ArtifactsResponse, BuildTargetResponse, DeclareBody, DeclareResponse, PromoteResponse, StatusResponse, UploadSlot, VerifyResponse, publishApiOf, type PublishApiAt } from "../internal/index.js";
26
26
  export { StudioPublishError, callJson, isTransientPublishError, } from "./callJson.js";
27
+ export { publishApiOf, type PublishApiAt };
27
28
  /** An artifact the live build has and this publish re-declares by hash. */
28
29
  export type CarriedArtifact = {
29
30
  key: string;
@@ -77,19 +78,6 @@ export type StudioPublishClient = {
77
78
  * short of the finish.
78
79
  */
79
80
  export declare function isExpiredSlot(error: unknown): boolean;
80
- /**
81
- * Where content's publish API is reached: through Val's proxy on this origin
82
- * (`api`, the Studio), or directly (`publishApi`, `<content>/v1`: home's
83
- * upgrade page, whose `fetchImpl` adds the project token).
84
- */
85
- export type PublishApiAt = {
86
- /** Val's API root on this origin, e.g. `/api/val`. */
87
- api: string;
88
- } | {
89
- /** Content's publish API itself, e.g. `https://content.val.build/v1`. */
90
- publishApi: string;
91
- };
92
- export declare function publishApiOf(at: PublishApiAt): string;
93
81
  export declare function createStudioPublishClient(options: PublishApiAt & {
94
82
  fetchImpl?: typeof fetch;
95
83
  }): StudioPublishClient;
@@ -339,7 +339,23 @@ function isValCanvasPageMessage(data) {
339
339
  if (_typeof(data) !== "object" || data === null) return false;
340
340
  var message = data;
341
341
  if (message.val !== VAL_CANVAS_MESSAGE) return false;
342
- return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "pageScreenshot" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
342
+ return message.type === "ready" && isModulePathList(data.holds) && isHeldVersions(data.holdsVersions) || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "pageScreenshot" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
343
+ }
344
+
345
+ /** `holdsVersions` on `ready`: absent, or a version per module path. */
346
+ function isHeldVersions(value) {
347
+ if (value === undefined) return true;
348
+ if (_typeof(value) !== "object" || value === null) return false;
349
+ return Object.values(value).every(function (version) {
350
+ return _typeof(version) === "object" && version !== null && typeof version.schema === "string" && typeof version.base === "string" && typeof version.chain === "string";
351
+ });
352
+ }
353
+
354
+ /** `holds` on `ready`: absent, or a list of module paths. */
355
+ function isModulePathList(value) {
356
+ return value === undefined || Array.isArray(value) && value.every(function (path) {
357
+ return typeof path === "string";
358
+ });
343
359
  }
344
360
  function isPageLocation(value) {
345
361
  var location = value;
@@ -404,7 +420,7 @@ function isValCanvasStudioMessage(data) {
404
420
  var read = data;
405
421
  return typeof read.requestId === "string" && (read.selector === undefined || typeof read.selector === "string") && (read.maxItems === undefined || typeof read.maxItems === "number");
406
422
  }
407
- return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced";
423
+ return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced" || message.type === "confirmed" && (data.seq === null || typeof data.seq === "number");
408
424
  }
409
425
 
410
426
  /**
@@ -359,7 +359,23 @@ function isValCanvasPageMessage(data) {
359
359
  if (createForOfIteratorHelper._typeof(data) !== "object" || data === null) return false;
360
360
  var message = data;
361
361
  if (message.val !== VAL_CANVAS_MESSAGE) return false;
362
- return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "pageScreenshot" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
362
+ return message.type === "ready" && isModulePathList(data.holds) && isHeldVersions(data.holdsVersions) || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "pageScreenshot" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
363
+ }
364
+
365
+ /** `holdsVersions` on `ready`: absent, or a version per module path. */
366
+ function isHeldVersions(value) {
367
+ if (value === undefined) return true;
368
+ if (createForOfIteratorHelper._typeof(value) !== "object" || value === null) return false;
369
+ return Object.values(value).every(function (version) {
370
+ return createForOfIteratorHelper._typeof(version) === "object" && version !== null && typeof version.schema === "string" && typeof version.base === "string" && typeof version.chain === "string";
371
+ });
372
+ }
373
+
374
+ /** `holds` on `ready`: absent, or a list of module paths. */
375
+ function isModulePathList(value) {
376
+ return value === undefined || Array.isArray(value) && value.every(function (path) {
377
+ return typeof path === "string";
378
+ });
363
379
  }
364
380
  function isPageLocation(value) {
365
381
  var location = value;
@@ -424,7 +440,7 @@ function isValCanvasStudioMessage(data) {
424
440
  var read = data;
425
441
  return typeof read.requestId === "string" && (read.selector === undefined || typeof read.selector === "string") && (read.maxItems === undefined || typeof read.maxItems === "number");
426
442
  }
427
- return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced";
443
+ return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced" || message.type === "confirmed" && (data.seq === null || typeof data.seq === "number");
428
444
  }
429
445
 
430
446
  /**
@@ -359,7 +359,23 @@ function isValCanvasPageMessage(data) {
359
359
  if (createForOfIteratorHelper._typeof(data) !== "object" || data === null) return false;
360
360
  var message = data;
361
361
  if (message.val !== VAL_CANVAS_MESSAGE) return false;
362
- return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "pageScreenshot" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
362
+ return message.type === "ready" && isModulePathList(data.holds) && isHeldVersions(data.holdsVersions) || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "pageScreenshot" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
363
+ }
364
+
365
+ /** `holdsVersions` on `ready`: absent, or a version per module path. */
366
+ function isHeldVersions(value) {
367
+ if (value === undefined) return true;
368
+ if (createForOfIteratorHelper._typeof(value) !== "object" || value === null) return false;
369
+ return Object.values(value).every(function (version) {
370
+ return createForOfIteratorHelper._typeof(version) === "object" && version !== null && typeof version.schema === "string" && typeof version.base === "string" && typeof version.chain === "string";
371
+ });
372
+ }
373
+
374
+ /** `holds` on `ready`: absent, or a list of module paths. */
375
+ function isModulePathList(value) {
376
+ return value === undefined || Array.isArray(value) && value.every(function (path) {
377
+ return typeof path === "string";
378
+ });
363
379
  }
364
380
  function isPageLocation(value) {
365
381
  var location = value;
@@ -424,7 +440,7 @@ function isValCanvasStudioMessage(data) {
424
440
  var read = data;
425
441
  return typeof read.requestId === "string" && (read.selector === undefined || typeof read.selector === "string") && (read.maxItems === undefined || typeof read.maxItems === "number");
426
442
  }
427
- return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced";
443
+ return message.type === "rescan" || message.type === "highlight" || message.type === "setPicking" || message.type === "sourceUpdate" || message.type === "sourcesSynced" || message.type === "confirmed" && (data.seq === null || typeof data.seq === "number");
428
444
  }
429
445
 
430
446
  /**
@@ -1,9 +1,176 @@
1
1
  'use strict';
2
2
 
3
+ var createForOfIteratorHelper = require('./createForOfIteratorHelper-f2d7ac6d.cjs.dev.js');
3
4
  var wrapNativeSuper = require('./wrapNativeSuper-933f991e.cjs.dev.js');
4
5
  var zod = require('zod');
5
6
  var zodValidationError = require('zod-validation-error');
6
7
 
8
+ /*
9
+ * The one JSON call the Studio makes through its own origin's publish proxy,
10
+ * and the errors it throws. Apart from `publishClient.ts` (which re-exports
11
+ * all three) so that a worker can make the same call without importing the
12
+ * publish client's parsers, and `@valbuild/shared` with them.
13
+ */
14
+
15
+ /**
16
+ * Who hears of a refusal's body before it is thrown: the Studio's shell, which
17
+ * says a refusal of the project's plan with the next step (`planRefusal.ts` in
18
+ * `@valbuild/ui`). One listener; setting it again replaces it.
19
+ */
20
+ var refusalListener = null;
21
+ function onPublishRefusal(listener) {
22
+ refusalListener = listener;
23
+ }
24
+
25
+ /** The publish API refused, and said why. */
26
+ var StudioPublishError = /*#__PURE__*/function (_Error) {
27
+ function StudioPublishError(statusCode, message, body) {
28
+ var _this;
29
+ wrapNativeSuper._classCallCheck(this, StudioPublishError);
30
+ _this = wrapNativeSuper._callSuper(this, StudioPublishError, [message]);
31
+ createForOfIteratorHelper._defineProperty(_this, "statusCode", void 0);
32
+ createForOfIteratorHelper._defineProperty(_this, "body", void 0);
33
+ _this.name = "StudioPublishError";
34
+ _this.statusCode = statusCode;
35
+ _this.body = body;
36
+ return _this;
37
+ }
38
+ wrapNativeSuper._inherits(StudioPublishError, _Error);
39
+ return wrapNativeSuper._createClass(StudioPublishError);
40
+ }(/*#__PURE__*/wrapNativeSuper._wrapNativeSuper(Error));
41
+
42
+ /**
43
+ * Was this a failure that asking again could get past?
44
+ *
45
+ * No answer at all (the request never got through) and the answers that mean
46
+ * "not now" -- a timeout, too many requests, a server or gateway error. Every
47
+ * other status is content answering, and the same request gets the same
48
+ * answer.
49
+ */
50
+ function isTransientPublishError(error) {
51
+ if (!(error instanceof StudioPublishError)) return true;
52
+ return error.statusCode >= 500 || error.statusCode === 408 || error.statusCode === 429;
53
+ }
54
+
55
+ /**
56
+ * One JSON call through this origin, with the session cookie: its body, or a
57
+ * `StudioPublishError` carrying the answer's own message.
58
+ */
59
+ function callJson(_x, _x2, _x3, _x4) {
60
+ return _callJson.apply(this, arguments);
61
+ }
62
+
63
+ /**
64
+ * One JSON READ through this origin, BY VERSION: `held` is the answer the
65
+ * caller holds, with its `ETag`, which goes as `If-None-Match`. While the
66
+ * answer is still that one (a 304, with no body) it is `held` again;
67
+ * otherwise the new body, `parse`d, with its `ETag` -- `null` when the answer
68
+ * named none, so there is nothing to hold it by. Errors as {@link callJson}
69
+ * throws them.
70
+ */
71
+ function _callJson() {
72
+ _callJson = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee(fetchImpl, url, method, body) {
73
+ var res;
74
+ return createForOfIteratorHelper._regenerator().w(function (_context) {
75
+ while (1) switch (_context.n) {
76
+ case 0:
77
+ _context.n = 1;
78
+ return fetchImpl(url, createForOfIteratorHelper._objectSpread2({
79
+ method: method,
80
+ // The session cookie is the whole credential this end has.
81
+ credentials: "same-origin"
82
+ }, body === undefined ? {} : {
83
+ headers: {
84
+ "Content-Type": "application/json"
85
+ },
86
+ body: JSON.stringify(body)
87
+ }));
88
+ case 1:
89
+ res = _context.v;
90
+ return _context.a(2, answerOf(res));
91
+ }
92
+ }, _callee);
93
+ }));
94
+ return _callJson.apply(this, arguments);
95
+ }
96
+ function readJsonByVersion(_x5, _x6, _x7, _x8) {
97
+ return _readJsonByVersion.apply(this, arguments);
98
+ }
99
+
100
+ /** An answer's JSON body, or a `StudioPublishError` with its own message. */
101
+ function _readJsonByVersion() {
102
+ _readJsonByVersion = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee2(fetchImpl, url, held, parse) {
103
+ var res, value, _t;
104
+ return createForOfIteratorHelper._regenerator().w(function (_context2) {
105
+ while (1) switch (_context2.n) {
106
+ case 0:
107
+ _context2.n = 1;
108
+ return fetchImpl(url, createForOfIteratorHelper._objectSpread2({
109
+ method: "GET",
110
+ // The session cookie is the whole credential this end has.
111
+ credentials: "same-origin"
112
+ }, held !== null ? {
113
+ headers: {
114
+ "If-None-Match": held.etag
115
+ }
116
+ } : {}));
117
+ case 1:
118
+ res = _context2.v;
119
+ if (!(res.status === 304 && held !== null)) {
120
+ _context2.n = 2;
121
+ break;
122
+ }
123
+ return _context2.a(2, held);
124
+ case 2:
125
+ _t = parse;
126
+ _context2.n = 3;
127
+ return answerOf(res);
128
+ case 3:
129
+ value = _t(_context2.v);
130
+ return _context2.a(2, {
131
+ etag: res.headers.get("etag"),
132
+ value: value
133
+ });
134
+ }
135
+ }, _callee2);
136
+ }));
137
+ return _readJsonByVersion.apply(this, arguments);
138
+ }
139
+ function answerOf(_x9) {
140
+ return _answerOf.apply(this, arguments);
141
+ }
142
+ function _answerOf() {
143
+ _answerOf = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee3(res) {
144
+ var text, parsed, _refusalListener, message;
145
+ return createForOfIteratorHelper._regenerator().w(function (_context3) {
146
+ while (1) switch (_context3.n) {
147
+ case 0:
148
+ _context3.n = 1;
149
+ return res.text();
150
+ case 1:
151
+ text = _context3.v;
152
+ try {
153
+ parsed = text === "" ? undefined : JSON.parse(text);
154
+ } catch (_unused) {
155
+ parsed = undefined;
156
+ }
157
+ if (res.ok) {
158
+ _context3.n = 2;
159
+ break;
160
+ }
161
+ // A publish the plan does not allow (a trial's 3 publishes, a trial that
162
+ // ended): the shell says it, with the next step. See `planRefusal`.
163
+ (_refusalListener = refusalListener) === null || _refusalListener === void 0 || _refusalListener(parsed);
164
+ message = createForOfIteratorHelper._typeof(parsed) === "object" && parsed !== null && "message" in parsed && typeof parsed.message === "string" ? parsed.message : "The publish API answered ".concat(res.status, ".");
165
+ throw new StudioPublishError(res.status, message, parsed);
166
+ case 2:
167
+ return _context3.a(2, parsed);
168
+ }
169
+ }, _callee3);
170
+ }));
171
+ return _answerOf.apply(this, arguments);
172
+ }
173
+
7
174
  function _objectWithoutPropertiesLoose(r, e) {
8
175
  if (null == r) return {};
9
176
  var t = {};
@@ -412,8 +579,61 @@ function parse(schema, body, call) {
412
579
  return parsed.data;
413
580
  }
414
581
 
582
+ /**
583
+ * Where content's publish API is reached: through Val's proxy on this origin
584
+ * (`api`, the Studio), or directly (`publishApi`, `<content>/v1`: home's
585
+ * upgrade page, whose `fetchImpl` adds the project token).
586
+ */
587
+
588
+ function publishApiOf(at) {
589
+ return "publishApi" in at ? at.publishApi.replace(/\/+$/, "") : "".concat(at.api.replace(/\/+$/, ""), "/publish-api");
590
+ }
591
+
592
+ /**
593
+ * The site's live source, BY VERSION: for a reader that asks again and again
594
+ * over a long life -- the Studio's code tools, on every tool call -- rather
595
+ * than once per deploy, as {@link createStudioPublishClient}'s memo does.
596
+ *
597
+ * Each call asks `/project-source` with the `ETag` of the answer it holds
598
+ * (`If-None-Match`), and content answers 304 while it is still that one
599
+ * (valbuild/home sends the tag: the sha256 of its answer). So a publish in
600
+ * between is seen on the next call, and the whole source is sent only when
601
+ * it changed. An answer without a tag (an older content service) is not
602
+ * held: every call reads it again, which is right, if dearer.
603
+ */
604
+ function createProjectSourceReader(options) {
605
+ var _options$fetchImpl;
606
+ var fetchImpl = (_options$fetchImpl = options.fetchImpl) !== null && _options$fetchImpl !== void 0 ? _options$fetchImpl : fetch;
607
+ var url = "".concat(publishApiOf(options), "/project-source");
608
+ var held = null;
609
+ return /*#__PURE__*/createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee() {
610
+ var answer;
611
+ return createForOfIteratorHelper._regenerator().w(function (_context) {
612
+ while (1) switch (_context.n) {
613
+ case 0:
614
+ _context.n = 1;
615
+ return readJsonByVersion(fetchImpl, url, held, function (body) {
616
+ return parseProjectSource(body).files;
617
+ });
618
+ case 1:
619
+ answer = _context.v;
620
+ held = answer.etag !== null ? {
621
+ etag: answer.etag,
622
+ value: answer.value
623
+ } : null;
624
+ return _context.a(2, answer.value);
625
+ }
626
+ }, _callee);
627
+ }));
628
+ }
629
+
415
630
  exports.PublishProtocolError = PublishProtocolError;
631
+ exports.StudioPublishError = StudioPublishError;
416
632
  exports._objectWithoutProperties = _objectWithoutProperties;
633
+ exports.callJson = callJson;
634
+ exports.createProjectSourceReader = createProjectSourceReader;
635
+ exports.isTransientPublishError = isTransientPublishError;
636
+ exports.onPublishRefusal = onPublishRefusal;
417
637
  exports.parseArtifacts = parseArtifacts;
418
638
  exports.parseBuildTarget = parseBuildTarget;
419
639
  exports.parseCancel = parseCancel;
@@ -431,3 +651,4 @@ exports.parseRequestStatus = parseRequestStatus;
431
651
  exports.parseStatus = parseStatus;
432
652
  exports.parseUpdateTarget = parseUpdateTarget;
433
653
  exports.parseVerify = parseVerify;
654
+ exports.publishApiOf = publishApiOf;