@valbuild/shared 0.148.7 → 0.149.7

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 (70) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/client/dist/valbuild-shared-client.cjs.dev.js +2 -1
  3. package/client/dist/valbuild-shared-client.cjs.prod.js +2 -1
  4. package/client/dist/valbuild-shared-client.esm.js +2 -1
  5. package/dist/acquireTypes-50960f2c.esm.js +1801 -0
  6. package/dist/acquireTypes-de10005d.cjs.dev.js +1815 -0
  7. package/dist/acquireTypes-e9467a2a.cjs.prod.js +1815 -0
  8. package/dist/createForOfIteratorHelper-3db5d737.esm.js +121 -0
  9. package/dist/createForOfIteratorHelper-90e6a8b6.cjs.prod.js +129 -0
  10. package/dist/createForOfIteratorHelper-d86bb352.cjs.dev.js +129 -0
  11. package/dist/declarations/src/internal/ApiRoutes.d.ts +3 -3
  12. package/dist/declarations/src/internal/index.d.ts +1 -0
  13. package/dist/declarations/src/internal/selfCheck.d.ts +51 -0
  14. package/dist/declarations/src/publish/buildProposal.d.ts +106 -0
  15. package/dist/declarations/src/publish/builder.d.ts +22 -0
  16. package/dist/declarations/src/publish/callJson.d.ts +21 -0
  17. package/dist/declarations/src/publish/index.d.ts +23 -0
  18. package/dist/declarations/src/publish/jobClient.d.ts +96 -0
  19. package/dist/declarations/src/publish/loadedLayer.d.ts +50 -0
  20. package/dist/declarations/src/publish/proposalProject.d.ts +2 -0
  21. package/dist/declarations/src/publish/publishClient.d.ts +95 -0
  22. package/dist/declarations/src/publish/runStudioDeploy.d.ts +244 -0
  23. package/dist/declarations/src/publish/runStudioJob.d.ts +102 -0
  24. package/dist/declarations/src/publish/runStudioPublish.d.ts +80 -0
  25. package/dist/declarations/src/typecheck/acquireTypes.d.ts +109 -0
  26. package/dist/declarations/src/typecheck/adeptSources.d.ts +9 -0
  27. package/dist/declarations/src/typecheck/index.d.ts +20 -0
  28. package/dist/declarations/src/typecheck/linkedFileSystem.d.ts +53 -0
  29. package/dist/declarations/src/typecheck/lockedVersions.d.ts +14 -0
  30. package/dist/declarations/src/typecheck/pnpmLayout.d.ts +95 -0
  31. package/dist/declarations/src/typecheck/pnpmLockfile.d.ts +57 -0
  32. package/dist/declarations/src/typecheck/proposalTypecheck.d.ts +76 -0
  33. package/dist/declarations/src/typecheck/testing/index.d.ts +6 -0
  34. package/dist/declarations/src/typecheck/testing/nodeModulesSources.d.ts +5 -0
  35. package/dist/declarations/src/typecheck/typecheckProject.d.ts +89 -0
  36. package/dist/declarations/src/typecheck/typescriptCompiler.d.ts +52 -0
  37. package/dist/{getSitemapTree-40d76bf8.cjs.prod.js → getSitemapTree-0fa0569c.cjs.dev.js} +5 -131
  38. package/dist/{getSitemapTree-cd006a36.esm.js → getSitemapTree-5afbc3ac.esm.js} +2 -121
  39. package/dist/{getSitemapTree-a7f2cc6d.cjs.dev.js → getSitemapTree-667bcb3c.cjs.prod.js} +5 -131
  40. package/dist/proposalProject-28550146.cjs.prod.js +18 -0
  41. package/dist/proposalProject-a3d3ad76.esm.js +16 -0
  42. package/dist/proposalProject-cdc53711.cjs.dev.js +18 -0
  43. package/dist/protocol-1c13de0d.cjs.prod.js +433 -0
  44. package/dist/protocol-3b9ef786.esm.js +413 -0
  45. package/dist/protocol-d8a6efda.cjs.dev.js +433 -0
  46. package/dist/slicedToArray-62e428df.cjs.dev.js +309 -0
  47. package/dist/slicedToArray-6cd80315.esm.js +299 -0
  48. package/dist/slicedToArray-af50e684.cjs.prod.js +309 -0
  49. package/internal/dist/valbuild-shared-internal.cjs.dev.js +419 -888
  50. package/internal/dist/valbuild-shared-internal.cjs.prod.js +419 -888
  51. package/internal/dist/valbuild-shared-internal.esm.js +236 -707
  52. package/package.json +27 -4
  53. package/publish/dist/valbuild-shared-publish.cjs.d.ts +2 -0
  54. package/publish/dist/valbuild-shared-publish.cjs.dev.js +2466 -0
  55. package/publish/dist/valbuild-shared-publish.cjs.js +7 -0
  56. package/publish/dist/valbuild-shared-publish.cjs.prod.js +2466 -0
  57. package/publish/dist/valbuild-shared-publish.esm.js +2428 -0
  58. package/publish/package.json +4 -0
  59. package/typecheck/dist/valbuild-shared-typecheck.cjs.d.ts +2 -0
  60. package/typecheck/dist/valbuild-shared-typecheck.cjs.dev.js +1173 -0
  61. package/typecheck/dist/valbuild-shared-typecheck.cjs.js +7 -0
  62. package/typecheck/dist/valbuild-shared-typecheck.cjs.prod.js +1173 -0
  63. package/typecheck/dist/valbuild-shared-typecheck.esm.js +1152 -0
  64. package/typecheck/package.json +4 -0
  65. package/typecheck/testing/dist/valbuild-shared-typecheck-testing.cjs.d.ts +2 -0
  66. package/typecheck/testing/dist/valbuild-shared-typecheck-testing.cjs.dev.js +202 -0
  67. package/typecheck/testing/dist/valbuild-shared-typecheck-testing.cjs.js +7 -0
  68. package/typecheck/testing/dist/valbuild-shared-typecheck-testing.cjs.prod.js +202 -0
  69. package/typecheck/testing/dist/valbuild-shared-typecheck-testing.esm.js +177 -0
  70. package/typecheck/testing/package.json +4 -0
@@ -0,0 +1,96 @@
1
+ import { type BuildTargetResponse, type JobStepBody, type PressResponse, type PublishRequestStatus, type PublishTabJob } from "../internal/index.js";
2
+ import { z } from "zod";
3
+ /**
4
+ * Publishing as a queued job, as a Studio tab speaks it.
5
+ *
6
+ * valbuild/home's docs/app-mode.md, "Publishing is a queued job", is the
7
+ * design: a press is a REQUEST that returns at once; the tab that pressed runs
8
+ * the job it is given -- prepare, build, upload -- and content runs verify and
9
+ * the seal. Everything goes through this deployment: the job routes through
10
+ * the publish proxy, and the prepare through this server's own route, which is
11
+ * the one place a job's sources can be computed.
12
+ */
13
+ export type StudioJobClient = {
14
+ /** A press of Publish. Idempotent on `requestId`. */
15
+ press(requestId: string, tab: string): Promise<PressResponse>;
16
+ /**
17
+ * Publish in a proposal: press the merge of `proposal`. Idempotent on
18
+ * `requestId`, as `press` is; the merge checks run first and may refuse it.
19
+ * From the press on it is a request like any other.
20
+ */
21
+ pressMerge(proposal: string, requestId: string, tab: string,
22
+ /**
23
+ * The checks the person chose to publish over, having been told what
24
+ * failed: `types`, an upgrade's new type errors (Publish anyway on home's
25
+ * upgrade page). Content refuses it over anything else.
26
+ */
27
+ anyway?: "types"[]): Promise<PressResponse>;
28
+ /** Try again: resume a paused queue and press anew. */
29
+ tryAgain(requestId: string, tab: string): Promise<PressResponse>;
30
+ /** Where a press is. */
31
+ requestStatus(requestId: string): Promise<PublishRequestStatus>;
32
+ /** A free tab asking for queued work. */
33
+ next(tab: string): Promise<PublishTabJob | null>;
34
+ /** The job's prepare, by this server: its sources, and content's archive of them. */
35
+ prepare(job: PublishTabJob, tab: string): Promise<PreparedJob>;
36
+ /** The tab reports a step it ran. */
37
+ step(jobId: string, body: JobStepBody): Promise<PublishTabJob | null>;
38
+ /** The tab is still building. `false`: it no longer holds the job. */
39
+ renew(jobId: string, tab: string): Promise<boolean>;
40
+ cancel(jobId: string): Promise<boolean>;
41
+ /** Discard a failed job's changes; answers what another job still holds. */
42
+ discard(jobId: string): Promise<string[]>;
43
+ /**
44
+ * Connected: the newest CI run the branch's workflow reported -- where
45
+ * "View run" goes for a build that failed. `null` when none was reported.
46
+ */
47
+ newestCiRun(): Promise<{
48
+ status: "failed" | "succeeded";
49
+ url: string | null;
50
+ } | null>;
51
+ };
52
+ /** What the tab builds a job from: `/api/val/publish-job-prepare`'s answer. */
53
+ export type PreparedJob = {
54
+ job: PublishTabJob | null;
55
+ sourceFiles: Record<string, string | null>;
56
+ binaryFiles: Record<string, string>;
57
+ binaryFilesUnread: string[];
58
+ branch: string | null;
59
+ /** Can this tab build the job from these sources? See the route's `buildable`. */
60
+ buildable: boolean;
61
+ /**
62
+ * What to build against, when it is not the site's: a merge of a proposal
63
+ * that changed its dependencies builds on the proposal's own framework and
64
+ * layer (valbuild/home `docs/adept.md`, step 6). Absent, the deploy asks
65
+ * the site's, as it always did.
66
+ */
67
+ target?: BuildTargetResponse;
68
+ };
69
+ export declare function createStudioJobClient(options: ({
70
+ api: string;
71
+ } | {
72
+ /** Content's publish API itself: see `PublishApiAt`. */
73
+ publishApi: string;
74
+ /** The project's proposals on content, `<content>/v1/<org>/<project>/proposals`. */
75
+ proposalsApi: string;
76
+ }) & {
77
+ fetchImpl?: typeof fetch;
78
+ }): StudioJobClient;
79
+ /** A job's change that merges a proposal: `merge:<name>` (content's `mergeChange`). */
80
+ export declare function isMergeChange(change: string): boolean;
81
+ /** `/publish-jobs/:id/merge-prepare`'s answer: the merge's files, from content. */
82
+ export declare const MergePrepareAnswer: z.ZodObject<{
83
+ job: z.ZodNullable<z.ZodObject<{
84
+ id: z.ZodString;
85
+ step: z.ZodNullable<z.ZodEnum<{
86
+ prepare: "prepare";
87
+ build: "build";
88
+ upload: "upload";
89
+ }>>;
90
+ base: z.ZodNullable<z.ZodString>;
91
+ patches: z.ZodArray<z.ZodString>;
92
+ contentOnly: z.ZodOptional<z.ZodLiteral<true>>;
93
+ }, z.core.$strip>>;
94
+ sourceFiles: z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>;
95
+ target: z.ZodOptional<z.ZodUnknown>;
96
+ }, z.core.$strip>;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Has the site moved onto other dependencies since this page loaded?
3
+ *
4
+ * A managed project's Studio is served by the site's own live build, so the
5
+ * Studio on screen -- its builder, its Val -- is the version that build was
6
+ * made with. When the site is updated (Settings → Updates, in this tab or
7
+ * another), every Studio opened before that is the OLD version, and a publish
8
+ * from one would build the site with the old builder, or, if it read its build
9
+ * target before the update went live, put the old dependencies back.
10
+ *
11
+ * So the page remembers which dependency layer the live build had when it
12
+ * loaded, and a publish compares that with the live build's now. They differ
13
+ * only when somebody moved the site's dependencies in between; the answer is
14
+ * to reload, which loses nothing -- unpublished changes are the server's.
15
+ *
16
+ * Read from `/__api/head`, which the platform answers on every site without a
17
+ * credential and which names the layer (`projectVendorRev`). Anything that
18
+ * cannot answer -- not on the platform, the loader unreachable, an older
19
+ * loader -- reads as "cannot tell", and "cannot tell" never blocks a publish.
20
+ */
21
+ /**
22
+ * The layer, or `undefined` for "cannot tell". `null` is never READ -- a head
23
+ * that names no layer is "cannot tell" too, see `readLiveLayer` -- but is what
24
+ * a build target says for a project with none, which is why it is comparable.
25
+ */
26
+ export type LayerReading = string | null | undefined;
27
+ export declare function readLiveLayer(fetchImpl?: typeof fetch): Promise<LayerReading>;
28
+ /**
29
+ * Stale only on a positive answer from both readings. A page that could not
30
+ * tell what it loaded on, or cannot tell now, publishes as before.
31
+ */
32
+ export declare function layerMoved(loaded: LayerReading, now: LayerReading): boolean;
33
+ /**
34
+ * Take the reading this page is compared against. Once per page: the first
35
+ * call wins, because a later one would record the layer an update just moved
36
+ * to and make a stale page look current.
37
+ */
38
+ export declare function rememberLoadedLayer(fetchImpl?: typeof fetch): void;
39
+ /** What {@link rememberLoadedLayer} read, or `undefined` if it never ran. */
40
+ export declare function loadedLayer(): Promise<LayerReading>;
41
+ /** Has the live build's layer moved since this page loaded? */
42
+ export declare function siteMovedSinceLoad(fetchImpl?: typeof fetch): Promise<boolean>;
43
+ /** The code a deploy refuses with when this page is out of date. */
44
+ export declare const STUDIO_OUT_OF_DATE = "STUDIO_OUT_OF_DATE";
45
+ export declare const STUDIO_OUT_OF_DATE_MESSAGE: string;
46
+ export declare function markStudioOutOfDate(): void;
47
+ export declare function studioOutOfDateRefusals(): number;
48
+ export declare function subscribeStudioOutOfDate(listener: () => void): () => void;
49
+ /** For tests: forget everything this module has learnt. */
50
+ export declare function resetLoadedLayerForTests(): void;
@@ -0,0 +1,2 @@
1
+ /** The proposal's project: the site's source, with what the proposal changed laid over it. */
2
+ export declare function proposalProject(site: Record<string, string>, own: Record<string, string | null>): Record<string, string>;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The Studio's side of the content service's publish API.
3
+ *
4
+ * Same conversation `val publish` has -- declare, upload, confirm, verify,
5
+ * promote -- and deliberately the same PARSERS: `parseDeclare` and the rest
6
+ * come from `@valbuild/shared/internal`, where they live precisely so that two
7
+ * publishers cannot come to disagree about what content said.
8
+ *
9
+ * ## Two things differ from the CLI's client, and both are the same fact
10
+ *
11
+ * A browser holds a session cookie for the app's origin and no credential
12
+ * content would accept -- content's publish API takes a project token and
13
+ * refuses everything else. So:
14
+ *
15
+ * - **Every call goes to this origin**, at `/api/val/publish-api`, and the
16
+ * deployment swaps the credential on the way (see `ValOpsHttp.publishApi`).
17
+ * Nothing here has a token, and nothing here should ever be given one.
18
+ * - **The uploads do not.** A slot is a presigned URL for object storage, and
19
+ * those bytes go straight there rather than through the isolate -- which is
20
+ * the whole reason the API hands out slots instead of accepting the artifacts
21
+ * itself. `credentials: "omit"` on those, because a presigned URL carries its
22
+ * own authorisation and a cookie sent to a storage host is a cookie leaked to
23
+ * one.
24
+ */
25
+ import { ArtifactsResponse, BuildTargetResponse, DeclareBody, DeclareResponse, PromoteResponse, StatusResponse, UploadSlot, VerifyResponse } from "../internal/index.js";
26
+ export { StudioPublishError, callJson, isTransientPublishError, } from "./callJson.js";
27
+ /** An artifact the live build has and this publish re-declares by hash. */
28
+ export type CarriedArtifact = {
29
+ key: string;
30
+ sha256: string;
31
+ bytes: number;
32
+ };
33
+ /** See {@link StudioPublishClient.publicFiles}. */
34
+ export type LivePublicFiles = {
35
+ carried: CarriedArtifact[];
36
+ } | {
37
+ paths: string[];
38
+ } | null;
39
+ /** Object storage refused the bytes, or could not be reached. */
40
+ export declare class StudioUploadError extends Error {
41
+ readonly statusCode: number;
42
+ readonly key: string;
43
+ constructor(statusCode: number, key: string, message: string);
44
+ }
45
+ export type StudioPublishClient = {
46
+ /**
47
+ * What to build against, and what to build -- the two reads a publish makes
48
+ * before it has anything to declare.
49
+ *
50
+ * On this type rather than beside it because they go through the same proxy
51
+ * with the same credential, and because a caller holding one of them and not
52
+ * the other can do nothing at all.
53
+ */
54
+ buildTarget(): Promise<BuildTargetResponse>;
55
+ /** `null` when the project has never published. */
56
+ projectSource(): Promise<Record<string, string> | null>;
57
+ /**
58
+ * What the live build serves from `public/`: by hash when content published
59
+ * it, by path when only the loader knows, `null` when neither can say. See
60
+ * `publicFiles` and `publicPaths` on `GET /v1/project-source`, which this
61
+ * reads with {@link projectSource} in one request.
62
+ */
63
+ publicFiles(): Promise<LivePublicFiles>;
64
+ declare(body: DeclareBody): Promise<DeclareResponse>;
65
+ confirmArtifacts(publishId: string): Promise<ArtifactsResponse>;
66
+ verify(publishId: string): Promise<VerifyResponse>;
67
+ promote(publishId: string): Promise<PromoteResponse>;
68
+ status(publishId: string): Promise<StatusResponse>;
69
+ /** The artifact's bytes, straight to object storage. */
70
+ upload(slot: UploadSlot, body: string): Promise<void>;
71
+ };
72
+ /**
73
+ * A slot's permission to write has run out.
74
+ *
75
+ * Its own shape because it is not a failure: the answer is to declare again for
76
+ * fresh slots, and a publish that treated it as one would give up an upload
77
+ * short of the finish.
78
+ */
79
+ 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
+ export declare function createStudioPublishClient(options: PublishApiAt & {
94
+ fetchImpl?: typeof fetch;
95
+ }): StudioPublishClient;
@@ -0,0 +1,244 @@
1
+ /**
2
+ * A build, from the Studio, handed to content.
3
+ *
4
+ * Two callers. A publish JOB's tab builds the job's content and stops once
5
+ * its artifacts are confirmed (`until: "confirmed"`): content verifies and
6
+ * seals it, and mints the commit then (valbuild/home, docs/app-mode.md,
7
+ * "Publishing is a queued job"). An UPDATE builds the live content against a
8
+ * new dependency layer and takes it live itself. Either way what is here is
9
+ * the part a managed project has nobody else to do -- read what to build and
10
+ * what to build it from, build it, and hand the result to content.
11
+ *
12
+ * ## Everything it cannot do itself is a parameter
13
+ *
14
+ * There are four, and each is something this package must not contain:
15
+ *
16
+ * - **`client`** reaches content through this origin's proxy. A browser holds
17
+ * a session cookie and no project token; the deployment swaps the credential.
18
+ * - **`loadBuilder`** is the dynamic import, replaceable because nothing that
19
+ * imports it for real runs under jest — `@rolldown/browser` is ESM-only and
20
+ * this repository's jest is CommonJS.
21
+ * - **`generateRouteTree`** is TanStack's generator plus babel, which is 2.6 MB
22
+ * and is not on npm. See {@link StudioDeployOptions.generateRouteTree}.
23
+ * - **`commit`** is the commit an update is wired at, which only the caller has.
24
+ *
25
+ * So this module is testable without any of them, which is the point: the
26
+ * sequence is where the mistakes are, and the sequence is what is left.
27
+ *
28
+ * ## It does not throw
29
+ *
30
+ * Same rule as `runStudioPublish`, and for the same reason: every failure here
31
+ * means one thing to an editor — nothing is live, and here is why — so a caller
32
+ * that had to distinguish eight rejections would write eight identical
33
+ * branches. The `problems` from content survive because they are the only part
34
+ * a person can act on.
35
+ */
36
+ import type { BuildTarget } from "@valbuild/tanstack-build";
37
+ import { PublishProblem } from "../internal/index.js";
38
+ import { CarriedArtifact, StudioPublishClient } from "./publishClient.js";
39
+ import { PublishPhase } from "./runStudioPublish.js";
40
+ import type { StudioBuilder } from "./builder.js";
41
+ import { LayerReading } from "./loadedLayer.js";
42
+ /**
43
+ * The binary files a commit wrote, as `/save` answered them.
44
+ *
45
+ * `unread` is files the server could not read back before committing. The
46
+ * build refuses over them: the site would link a file it does not serve.
47
+ */
48
+ export type CommittedBinaryFiles = {
49
+ files: Record<string, string>;
50
+ unread: string[];
51
+ };
52
+ export type DeployPhase =
53
+ /**
54
+ * A publish job waiting its turn: queued behind the one before it. Never
55
+ * reported by the deploy itself -- it is how the Studio shows a press whose
56
+ * job has not started (see `publishProgress`).
57
+ */
58
+ {
59
+ kind: "queued";
60
+ }
61
+ /** Waiting for the bundler. Only ever seen when the preload has not landed. */
62
+ | {
63
+ kind: "getting-ready";
64
+ } | {
65
+ kind: "reading";
66
+ } | {
67
+ kind: "building";
68
+ } | PublishPhase;
69
+ export type StudioDeployResult = {
70
+ status: "live";
71
+ url: string | null;
72
+ } | {
73
+ status: "already-live";
74
+ url: string | null;
75
+ }
76
+ /** A publish job's build, uploaded and confirmed: content does the rest. */
77
+ | {
78
+ status: "uploaded";
79
+ publishId: string;
80
+ } | {
81
+ status: "failed";
82
+ message: string;
83
+ problems: PublishProblem[];
84
+ };
85
+ export interface StudioDeployOptions {
86
+ client: StudioPublishClient;
87
+ /**
88
+ * For a publish job's tab: stop once the artifacts are confirmed, and
89
+ * answer with the publish id. Content runs verify and the seal itself, and
90
+ * `commit` is `null` -- a job's build is wired at no commit, because its
91
+ * commit is minted at the seal (valbuild/home, docs/app-mode.md: "A
92
+ * platform build bakes nothing and asks content").
93
+ */
94
+ until?: "confirmed";
95
+ /**
96
+ * The commit `/save` just made, or `null` for a publish from no commit.
97
+ *
98
+ * The BRANCH is deliberately not here, because the caller has no honest
99
+ * source for one: `/stat` does not carry it and neither does `/save`, and a
100
+ * branch the browser invented is a branch content would be asked to record a
101
+ * build against on the browser's say-so. It is read out of the project's own
102
+ * `val.server.ts` instead — see {@link branchOf} — which is where the last
103
+ * build put it and which does not change between publishes.
104
+ *
105
+ * The commit is baked in before the build, and that is not bookkeeping: Val
106
+ * reads content from the content service AT A COMMIT, so a build wired at
107
+ * the previous one renders and edits a different version of its files than
108
+ * the code it is running.
109
+ */
110
+ commit: string | null;
111
+ /**
112
+ * The publish job this build is for, baked in beside the commit so the
113
+ * build can say where it sits in the content service's chain -- it has no
114
+ * commit to say it with (`BUILT_FOR_JOB` in `@valbuild/tanstack-build`).
115
+ * Left out by a build that is not a job's (an update), which keeps the job
116
+ * of the source it was made from: an update changes no content.
117
+ */
118
+ job?: string | null;
119
+ /**
120
+ * The source files to build, by path, over the project's stored source --
121
+ * for a publish job, every file of the job's content, as the server's
122
+ * `/publish-job-prepare` rendered it -- or `null` when there are none to add
123
+ * (an update, which changes no content).
124
+ *
125
+ * Laid over the stored source before anything else, because the stored
126
+ * source is what the LAST build was made from: without this the build
127
+ * publishes the site as it was before the change. `null` values are files
128
+ * the change deleted. Paths may carry a leading `/` (Val's module paths do)
129
+ * and are matched without it.
130
+ */
131
+ committedFiles?: Record<string, string | null> | null;
132
+ /**
133
+ * The binary files that commit wrote -- an image uploaded in this save --
134
+ * base64 by path, or `null` when there are none to add.
135
+ *
136
+ * Nothing else has them: the stored source is text, and the live build
137
+ * predates the upload. A file under `public/` is published as one; anything
138
+ * else goes to the build as an asset, where an import can find it.
139
+ */
140
+ committedBinaryFiles?: CommittedBinaryFiles | null;
141
+ /**
142
+ * The branch the commit is on, as the server had it from the content
143
+ * service, or `null` when it did not say.
144
+ *
145
+ * Preferred over the branch baked into the last build's `val.server.ts`,
146
+ * which is the fallback: a project cloned from a template seed was wired at
147
+ * no commit and no branch, and a branchless publish is refused by a loader
148
+ * whose pointer follows one -- which admin configures for every project.
149
+ */
150
+ branch?: string | null;
151
+ /**
152
+ * The live site's compiled stylesheet, used when this build produced none.
153
+ *
154
+ * A build in the browser cannot run Tailwind `@plugin`s, and the stored
155
+ * source may not carry the stylesheets at all. A Studio save changes content
156
+ * and uploaded files, never a stylesheet or a component, so the live CSS is
157
+ * still the right CSS; without this every Studio publish shipped unstyled.
158
+ */
159
+ liveStylesheet?: () => Promise<string>;
160
+ /**
161
+ * One of the live site's public files, base64, by its URL path
162
+ * (`favicon.ico` for `/favicon.ico`).
163
+ *
164
+ * Only asked for when content cannot name the live build's public files by
165
+ * hash and the loader can name them by path. Absent, that case is refused
166
+ * like the one where nobody can say.
167
+ */
168
+ fetchPublicFile?: (path: string) => Promise<string>;
169
+ /**
170
+ * What to build against, when it is not what the live build was built
171
+ * against.
172
+ *
173
+ * Absent for every publish but one: an update, which moves the project onto
174
+ * its template's current dependency layer. The live build's target names the
175
+ * layer it is leaving, so the update's own answer is used instead -- and the
176
+ * publish then names the layer it carries, which is what moves the site.
177
+ */
178
+ target?: BuildTarget;
179
+ /**
180
+ * The dependency layer the live build had when this page loaded (see
181
+ * `loadedLayer.ts`), or `undefined` when it cannot say.
182
+ *
183
+ * A publish whose build target names a different one is refused: the site
184
+ * was updated since, this Studio is the old version, and building with it
185
+ * would use the old builder. Not asked of an update, whose whole point is a
186
+ * different layer.
187
+ */
188
+ loadedLayer?: LayerReading;
189
+ /**
190
+ * The live build's layer NOW. Asked once more after the build, just before
191
+ * the declare: an update that went live while this page was building would
192
+ * pass the check against the target read at the start, and this publish
193
+ * would then put the old dependencies back. Absent, only that first check
194
+ * is made. What closes the remaining window -- declare to promote -- is the
195
+ * seal in publish-jobs.
196
+ */
197
+ liveLayer?: () => Promise<LayerReading>;
198
+ loadBuilder: () => Promise<StudioBuilder>;
199
+ /**
200
+ * Generates `src/routeTree.gen.ts` for a file-based project.
201
+ *
202
+ * A capability, like `routeSplitter` and `loadCssModule` on `BuildInput` and
203
+ * for the same reason: it is TanStack's generator over babel, it is 2.6 MB
204
+ * built for a browser, and it is not published. A deployment that has one
205
+ * supplies it.
206
+ *
207
+ * Absent, a file-based project is REFUSED by name. It would otherwise fail
208
+ * inside rolldown with `UNRESOLVED_ENTRY`, because a file-based project's
209
+ * entry IS the generated tree — a message about a module specifier, for a
210
+ * missing capability, several layers from anything anyone can act on.
211
+ */
212
+ generateRouteTree?: (files: Record<string, string>) => Promise<Record<string, string>>;
213
+ onPhase: (phase: DeployPhase) => void;
214
+ }
215
+ export declare function runStudioDeploy(options: StudioDeployOptions): Promise<StudioDeployResult>;
216
+ /**
217
+ * The stored source with a commit's files laid over it.
218
+ *
219
+ * A new record rather than an edit of the one read, which the caller may still
220
+ * hold. Val names module files with a leading `/`; the stored record does
221
+ * not, so the slash is dropped -- two spellings of one path would build the
222
+ * old file and publish the new one beside it.
223
+ */
224
+ export declare function withCommittedFiles(source: Record<string, string>, committed: Record<string, string | null> | null): Record<string, string>;
225
+ /**
226
+ * A commit's binary files, as the build takes them: `public/...` served as is,
227
+ * everything else an asset an import can reach. Keys lose the leading `/`
228
+ * Val's paths carry, as in {@link withCommittedFiles}.
229
+ */
230
+ export declare function splitBinaryFiles(files: Record<string, string>): {
231
+ publicFiles: Record<string, string>;
232
+ assets: Record<string, string>;
233
+ };
234
+ /**
235
+ * The live build's public files this build keeps, re-declared by hash.
236
+ *
237
+ * All of them, except the ones this build produced itself (a file the commit
238
+ * replaced) and the ones the commit deleted. Content holds the bytes already,
239
+ * so this uploads nothing -- and if it has lost one, it asks for it, and
240
+ * `runStudioPublish` refuses rather than publishing a hole.
241
+ */
242
+ export declare function carriedInto(built: ReadonlyArray<{
243
+ key: string;
244
+ }>, carried: ReadonlyArray<CarriedArtifact>, committed: Record<string, string | null> | null): CarriedArtifact[];
@@ -0,0 +1,102 @@
1
+ import type { PublishTabJob } from "../internal/index.js";
2
+ import type { DeployPhase, StudioDeployResult } from "./runStudioDeploy.js";
3
+ import type { PreparedJob, StudioJobClient } from "./jobClient.js";
4
+ /**
5
+ * A publish job, as the tab that holds it runs it: prepare, build, upload --
6
+ * and then it is content's (valbuild/home, docs/app-mode.md, "Publishing is a
7
+ * queued job"). Content runs verify and the seal, and "Live" arrives on the
8
+ * websocket, so a tab that closes after its upload costs nothing.
9
+ *
10
+ * The tab holds the job by a lease, renewed on a timer while it works. A
11
+ * renewal answered `false` means the job is no longer this tab's -- its lease
12
+ * lapsed and its requests went back to the queue, or it was cancelled -- and
13
+ * the tab stops: another tab, or this one's next job, will build it.
14
+ *
15
+ * Every step is reported by name, so a report retried after a lost answer
16
+ * does nothing twice, and a failure is reported as the step's: content counts
17
+ * the attempts and, after the last, fails the job with Try again.
18
+ */
19
+ export type JobPhase = {
20
+ kind: "preparing";
21
+ } | {
22
+ kind: "deploying";
23
+ phase: DeployPhase;
24
+ } | {
25
+ kind: "handing-off";
26
+ };
27
+ export type StudioJobResult =
28
+ /**
29
+ * The tab's part is done, and content has the job. `built`: this tab built
30
+ * and uploaded it. `false` is a job whose build is CI's -- a connected
31
+ * project's, where the tab's part is the prepare alone -- or, with
32
+ * `contentOnly`, a job nobody builds: its content goes live on the build
33
+ * already live.
34
+ */
35
+ {
36
+ status: "handed-off";
37
+ jobId: string;
38
+ built: boolean;
39
+ contentOnly?: true;
40
+ }
41
+ /** The job is no longer this tab's. Nothing to report. */
42
+ | {
43
+ status: "lost";
44
+ jobId: string;
45
+ }
46
+ /**
47
+ * A managed job that needs a build, prepared by a tab that cannot run the
48
+ * builder (`buildsElsewhere`). Not reported: its lease lapses, it goes back
49
+ * to the queue, and a builder tab -- opened with one more tap -- takes it.
50
+ */
51
+ | {
52
+ status: "needs-builder";
53
+ jobId: string;
54
+ }
55
+ /** A step failed, and was reported as failed. */
56
+ | {
57
+ status: "failed";
58
+ jobId: string;
59
+ message: string;
60
+ };
61
+ /** How often the tab renews its lease. Content's job lease is 30 s. */
62
+ export declare const RENEW_EVERY_MS = 10000;
63
+ export declare function runStudioJob(options: {
64
+ client: StudioJobClient;
65
+ job: PublishTabJob;
66
+ tab: string;
67
+ /**
68
+ * Build the job from its prepared sources, stopping once the artifacts are
69
+ * confirmed: `runStudioDeploy` with `until: "confirmed"` and no commit.
70
+ */
71
+ deploy: (prepared: PreparedJob, onPhase: (phase: DeployPhase) => void) => Promise<StudioDeployResult>;
72
+ onPhase: (phase: JobPhase) => void;
73
+ renewEveryMs?: number;
74
+ /**
75
+ * Can this tab build at all? Connected only: a tab that cannot (no
76
+ * cross-origin isolation) reports "no build", and CI builds the push. A
77
+ * managed job never reaches a tab that cannot -- it is handed to one that can.
78
+ */
79
+ canBuild?: () => boolean;
80
+ /**
81
+ * Managed, and a tab that cannot build: an iPhone, or the overlay, running
82
+ * its own publish so that one of content alone needs no tab. A job that
83
+ * needs a build after all is given back (`needs-builder`) rather than said
84
+ * to need none -- content refuses that for a managed job, which has no CI.
85
+ */
86
+ buildsElsewhere?: boolean;
87
+ }): Promise<StudioJobResult>;
88
+ /**
89
+ * Run a job until this tab's part of it is over: handed off, lost, or failed
90
+ * for good.
91
+ *
92
+ * A failed step is counted by content, which keeps the job at that step for
93
+ * the tab to run again -- until its last attempt, when it fails the job.
94
+ * Whether it is still this tab's is the renewal's answer, so a failure is
95
+ * followed by one, and a `true` runs it again.
96
+ */
97
+ export declare function runJobToEnd(options: {
98
+ client: Pick<StudioJobClient, "renew">;
99
+ job: PublishTabJob;
100
+ tab: string;
101
+ run: () => Promise<StudioJobResult>;
102
+ }): Promise<StudioJobResult>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Publishing what the tab just built.
3
+ *
4
+ * The same conversation `val publish` has, with the same parsers, and the same
5
+ * shape of state machine -- declare, upload what content does not already hold,
6
+ * confirm, verify, promote. It is written out again here rather than shared
7
+ * with the CLI's `runPublish` because the two differ in every input and in what
8
+ * they do when something goes wrong: the CLI reads artifacts off a disk it
9
+ * controls, takes a token, prints lines and sets an exit code; this holds them
10
+ * in memory, has no credential, and has to leave an EDITOR with something they
11
+ * can act on.
12
+ *
13
+ * What is shared is what must not drift: the wire shapes and their parsers
14
+ * (`@valbuild/shared/internal`) and the artifact key namespace
15
+ * (`@valbuild/tanstack-build`). Those are the parts where a second, slightly
16
+ * different copy is how two publishers come to disagree with one service.
17
+ *
18
+ * ## No progress reporting beyond phases, on purpose
19
+ *
20
+ * Byte-level upload progress would need `XMLHttpRequest` per artifact, and a
21
+ * publish is dozens of small ones rather than one big one -- so what a person
22
+ * sees from counting artifacts is the same and the code is a quarter the size.
23
+ *
24
+ * ## Why it never throws
25
+ *
26
+ * Every exit is a value. A publish that failed has to say WHICH step and leave
27
+ * the commit intact; a thrown error at an await boundary in a React handler is
28
+ * an unhandled rejection and a spinner that never stops.
29
+ */
30
+ import type { PublishArtifact } from "@valbuild/tanstack-build";
31
+ import { type DeclareBody, type PublishProblem } from "../internal/index.js";
32
+ import { StudioPublishClient } from "./publishClient.js";
33
+ /** Which step the publish is on, for the one line an editor is shown. */
34
+ export type PublishPhase = {
35
+ kind: "declaring";
36
+ } | {
37
+ kind: "uploading";
38
+ done: number;
39
+ total: number;
40
+ } | {
41
+ kind: "confirming";
42
+ } | {
43
+ kind: "verifying";
44
+ } | {
45
+ kind: "promoting";
46
+ };
47
+ export type StudioPublishResult = {
48
+ status: "live";
49
+ publishId: string;
50
+ url: string | null;
51
+ }
52
+ /** Content had already taken this exact build. Not a failure. */
53
+ | {
54
+ status: "already-live";
55
+ publishId: string;
56
+ url: string | null;
57
+ }
58
+ /**
59
+ * Stopped after the artifacts were confirmed, as `until: "confirmed"` asks:
60
+ * a publish JOB's tab does no more than that, and content runs verify and
61
+ * the seal (valbuild/home, docs/app-mode.md, "Publishing is a queued job").
62
+ */
63
+ | {
64
+ status: "uploaded";
65
+ publishId: string;
66
+ } | {
67
+ status: "failed";
68
+ /** `null` when it failed before content had accepted anything. */
69
+ publishId: string | null;
70
+ message: string;
71
+ problems: PublishProblem[];
72
+ };
73
+ export declare function runStudioPublish(args: {
74
+ client: StudioPublishClient;
75
+ artifacts: PublishArtifact[];
76
+ declare: DeclareBody;
77
+ onPhase: (phase: PublishPhase) => void;
78
+ /** Stop once the artifacts are confirmed: for a publish job's tab. */
79
+ until?: "confirmed";
80
+ }): Promise<StudioPublishResult>;