@valbuild/shared 0.136.0 → 0.136.1

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,54 @@
1
1
  # @valbuild/shared
2
2
 
3
+ ## 0.136.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#716](https://github.com/valbuild/val/pull/716) [`e673b43`](https://github.com/valbuild/val/commit/e673b43feaf48b7f30881d6786c858a0cb6a44a2) Thanks [@freekh](https://github.com/freekh)! - The Studio can run a whole publish: read what to build and what to build it from, build it, and hand the result to the content service.
8
+
9
+ The two reads go through the same proxy and the same credential as the publish conversation, and both are validated rather than cast — an error page from a gateway in between would otherwise fail several layers down inside the bundler with a message about a module specifier.
10
+
11
+ Generating a route tree for a file-based project is a capability the deployment supplies, like `routeSplitter` and `loadCssModule` already are. Without one, such a project is refused by name rather than failing with `UNRESOLVED_ENTRY`.
12
+
13
+ - [#697](https://github.com/valbuild/val/pull/697) [`5c82928`](https://github.com/valbuild/val/commit/5c82928992d29f133f81cf0704a273c0449b18f1) Thanks [@freekh](https://github.com/freekh)! - Read draft content once per request instead of once per `fetchVal`
14
+
15
+ In draft mode, `fetchVal` resolves its selector by asking the Val server for the
16
+ whole module tree. It did that on every call, so a page with three reads made
17
+ three requests for three identical answers — and `fetchValRouteUrl` added a
18
+ fourth by calling `fetchVal` again on top of the caller's own. The answer cannot
19
+ differ between reads in one request, so it is now read once and shared. On a
20
+ 60-module project a three-read draft render goes from ~187ms to ~70ms.
21
+
22
+ The memo is scoped to the request and nothing wider, because the response it
23
+ holds contains the caller's own unpublished patches: React's `cache()` in a
24
+ Next RSC, a `WeakMap` keyed on the `Request` in TanStack Start, and no caching
25
+ at all where neither is available.
26
+
27
+ What this does **not** change:
28
+
29
+ - **Production is untouched.** With Val disabled the reader never calls the
30
+ server; it resolves against the statically imported module, as before.
31
+ - **What a draft shows is unchanged.** Same query, same patch scoping — a draft
32
+ still shows the caller's own staged work and nobody else's.
33
+ - **The client readers are untouched.** `useVal` already subscribed to exactly
34
+ the modules its selector names.
35
+ - **The server still evaluates every module per request.** Narrowing the
36
+ request's path was measured and does not help: `/sources/~` evaluates,
37
+ previews and validates everything and only then filters the response, so a
38
+ narrow path returns less for the same milliseconds. That remains open.
39
+
40
+ - [#716](https://github.com/valbuild/val/pull/716) [`d2f385a`](https://github.com/valbuild/val/commit/d2f385a04978992a5036379235e447ba7775faef) Thanks [@freekh](https://github.com/freekh)! - The Studio works again when served from the published package, and a managed project's publish now puts the saved edit on the site.
41
+
42
+ - **The Studio did not start in 0.136.0.** Its main bundle imports sibling chunks by relative path, and the Studio is served at `/api/val/static/<version>/app`, so those imports resolved to paths the handler did not know and came back as the HTML fallback — which a browser refuses as a module script. Preview was gone with it. The handler now serves those chunks, and the package build follows every import the bundle makes before it lets a release through.
43
+ - **The in-browser builder could not load.** The Studio's bundle carried rolldown's Node WASI binding, which throws `process is not defined` in a tab. It now uses the browser binding, and the build refuses a bundle that contains the Node one.
44
+ - **A managed publish builds what was just saved.** `/save` returns the source files the commit wrote, the Studio builds with them laid over the project's stored source, and publishes that source back with the build — so the next edit starts from this one rather than undoing it. The save's commit is also passed through to the build, which previously ran as though no commit had been made.
45
+ - **The bundler's WebAssembly is served from `content.val.build/v1/static`** (`DEFAULT_STATIC_HOST`), still addressed by its SHA-256. `globalThis.__VAL_ROLLDOWN_WASM_URL__` still overrides it.
46
+ - The `val.server.ts` that `@valbuild/tanstack-build` generates no longer hands a save's files to the platform (`platform.internal/__api/source`). A managed project's Studio builds in its own tab, and the platform has closed that door. `isWired` recognises files wired either way.
47
+ - `@valbuild/tanstack-build/constants` exports the contract paths and the artifact key namespace from an entrypoint that imports nothing else, for callers that must not load the bundler. The namespace gains a `source` key.
48
+
49
+ - Updated dependencies [[`d2f385a`](https://github.com/valbuild/val/commit/d2f385a04978992a5036379235e447ba7775faef)]:
50
+ - @valbuild/core@0.136.1
51
+
3
52
  ## 0.136.0
4
53
 
5
54
  ### Minor Changes
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var getSitemapTree = require('../../dist/getSitemapTree-99c97c62.cjs.dev.js');
5
+ var getSitemapTree = require('../../dist/getSitemapTree-c732e09a.cjs.dev.js');
6
6
  require('@valbuild/core');
7
7
 
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var getSitemapTree = require('../../dist/getSitemapTree-99839b02.cjs.prod.js');
5
+ var getSitemapTree = require('../../dist/getSitemapTree-0ed3deac.cjs.prod.js');
6
6
  require('@valbuild/core');
7
7
 
8
8
 
@@ -1,2 +1,2 @@
1
- export { D as DEFAULT_CANVAS_SELECTION, e as DEFAULT_CANVAS_SELECTION_SOFT, b as VAL_AI_SESSION_STORAGE_KEY, d as VAL_CANVAS_MESSAGE, c as VAL_CANVAS_PARAM, V as VAL_CONFIG_SESSION_STORAGE_KEY, a as VAL_THEME_SESSION_STORAGE_KEY, h as getNextAppRouterSourceFolder, m as getPageRouterSitemapTree, k as getPageRouterSourceFolder, n as getPatternFromModuleFilePath, j as getTanStackRouterSourceFolder, l as isTanStackRoutesFolder, g as isValCanvasFrame, i as isValCanvasPageMessage, f as isValCanvasStudioMessage, p as parseRoutePattern, w as withValCanvasParam } from '../../dist/getSitemapTree-878d3888.esm.js';
1
+ export { D as DEFAULT_CANVAS_SELECTION, e as DEFAULT_CANVAS_SELECTION_SOFT, b as VAL_AI_SESSION_STORAGE_KEY, d as VAL_CANVAS_MESSAGE, c as VAL_CANVAS_PARAM, V as VAL_CONFIG_SESSION_STORAGE_KEY, a as VAL_THEME_SESSION_STORAGE_KEY, h as getNextAppRouterSourceFolder, m as getPageRouterSitemapTree, k as getPageRouterSourceFolder, n as getPatternFromModuleFilePath, j as getTanStackRouterSourceFolder, l as isTanStackRoutesFolder, g as isValCanvasFrame, i as isValCanvasPageMessage, f as isValCanvasStudioMessage, p as parseRoutePattern, w as withValCanvasParam } from '../../dist/getSitemapTree-6fcdf745.esm.js';
2
2
  import '@valbuild/core';
@@ -476,6 +476,63 @@ export declare const Api: {
476
476
  }, z.core.$strip>]>;
477
477
  };
478
478
  };
479
+ /**
480
+ * The content service's publish API, reached through this deployment.
481
+ *
482
+ * A managed project is built in the Studio's own tab -- there is no host
483
+ * watching a repository to pick a commit up -- and what it builds then has to
484
+ * be published. The browser cannot do that itself: it holds a session cookie
485
+ * for this origin and no credential content would accept. So the publish
486
+ * conversation goes through here, exactly as patches already do.
487
+ *
488
+ * ## Deliberately not typed past this point
489
+ *
490
+ * No `res`, and a body this does not describe. The shapes belong to content,
491
+ * and they are already written down twice -- once in content's own `Api.ts`
492
+ * and once in the copy `@valbuild/cli` keeps beside its parser so the two can
493
+ * be diffed by eye. A third copy here would be a third thing to keep in step,
494
+ * and this layer has no decision to make that would need one: it authenticates
495
+ * the caller, swaps the credential, and carries status and body back verbatim.
496
+ * The Studio parses the answer with the same module the CLI does.
497
+ *
498
+ * That means this route answers with statuses the generic result type does not
499
+ * model -- a 409 when a publish has already gone live, for one. `/save` has
500
+ * always done the same; the dispatch boundary erases the type and the adapter
501
+ * sends what it is given.
502
+ *
503
+ * ## What it may reach
504
+ *
505
+ * The sub-path is content's, minus `/v1`: `/publish`, `/publish/{id}` and its
506
+ * three steps, and `/build-target`. `ValOpsHttp.publishApi` holds the allow
507
+ * list, beside the credential, because the browser chooses this path.
508
+ */
509
+ "/publish-api": {
510
+ GET: {
511
+ req: {
512
+ path: z.ZodOptional<z.ZodString>;
513
+ cookies: {
514
+ val_session: z.ZodOptional<z.ZodString>;
515
+ };
516
+ };
517
+ res: z.ZodObject<{
518
+ status: z.ZodNumber;
519
+ json: z.ZodUnknown;
520
+ }, z.core.$strip>;
521
+ };
522
+ POST: {
523
+ req: {
524
+ path: z.ZodOptional<z.ZodString>;
525
+ body: z.ZodOptional<z.ZodUnknown>;
526
+ cookies: {
527
+ val_session: z.ZodOptional<z.ZodString>;
528
+ };
529
+ };
530
+ res: z.ZodObject<{
531
+ status: z.ZodNumber;
532
+ json: z.ZodUnknown;
533
+ }, z.core.$strip>;
534
+ };
535
+ };
479
536
  "/upload/patches": {
480
537
  POST: {
481
538
  req: {
@@ -1019,6 +1076,7 @@ export declare const Api: {
1019
1076
  status: z.ZodLiteral<200>;
1020
1077
  json: z.ZodObject<{
1021
1078
  commitSha: z.ZodOptional<z.ZodString>;
1079
+ sourceFiles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodString>>>;
1022
1080
  removed: z.ZodOptional<z.ZodArray<z.ZodObject<{
1023
1081
  patchId: z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>;
1024
1082
  moduleFilePath: z.ZodString & z.ZodType<ModuleFilePath, string, z.core.$ZodTypeInternals<ModuleFilePath, string>>;
@@ -1,5 +1,6 @@
1
1
  export * from "./richtext/conversion/index.js";
2
2
  export * from "./server/types.js";
3
+ export * from "./server/requestScopedMemo.js";
3
4
  export * from "./ValClient.js";
4
5
  export * from "./ValUrls.js";
5
6
  export * from "./ApiRoutes.js";
@@ -32,3 +33,5 @@ export * from "./search/traverseSchemaSource.js";
32
33
  export * from "./search/getFilenameFromRef.js";
33
34
  export * from "./search/sourcePath.js";
34
35
  export * from "./theme/accentRamp.js";
36
+ export * from "./publish/contentApi.js";
37
+ export * from "./publish/protocol.js";
@@ -0,0 +1,201 @@
1
+ /**
2
+ * The publish API's types, COPIED from the service that serves them.
3
+ *
4
+ * Source: `content/src/handlers/Api.ts` in valbuild/home, branch
5
+ * `claude/new-project-studio-saves-0g9ksb`, commit `ecf3b8d`. The routes below
6
+ * are that file's `/publish*` entries and the two types they use, verbatim,
7
+ * comments included, so that the two can be diffed by eye.
8
+ *
9
+ * **Copied rather than imported, because it cannot be imported.** That file
10
+ * lives in a private repository which publishes nothing to npm, and its own
11
+ * header says this is how it is kept in step: "We have also used it (by just
12
+ * copying it in and setting the types there) in the @valbuild/server package to
13
+ * check that we are more or less in sync."
14
+ *
15
+ * **When home's `Api.ts` changes, change this with it.** Everything in
16
+ * `protocol.ts` is checked against these types, so a copy brought up to date
17
+ * fails to compile wherever this CLI has not caught up. That is the whole
18
+ * value: without it a wire change is a runtime 500 in somebody's CI, which is
19
+ * how `home` and `@valbuild/server` have already diverged three times - see
20
+ * `homeWireContract.test.ts` in `@valbuild/server` for the last one, and
21
+ * `publishWireContract.test.ts` here for the fixtures that go with these types.
22
+ *
23
+ * A copy is not a guarantee, only a tripwire: nothing checks it against the
24
+ * service, and the parsers in `protocol.ts` are what actually holds at runtime.
25
+ */
26
+ export type ContentPublishApi = {
27
+ /**
28
+ * Publishing a build, as a resource with a lifecycle.
29
+ *
30
+ * ```
31
+ * POST /publish declare -> an upload slot per MISSING artifact
32
+ * PUT <presigned url> upload -> each artifact, straight to storage
33
+ * POST /publish/{id}/artifacts confirm -> the uploads are checked
34
+ * POST /publish/{id}/verify render -> a canary build, server side
35
+ * POST /publish/{id}/promote go live -> the pointer moves
36
+ * GET /publish/{id} status
37
+ * ```
38
+ *
39
+ * Four steps rather than one `POST /publish`, because they fail differently
40
+ * and one call cannot say "the bytes are fine but it did not render" -- which
41
+ * is the sentence a publisher most needs. `promote` is separate from `verify`
42
+ * so that a dry run is the absence of a call rather than a flag.
43
+ *
44
+ * Not keyed by a project, like `/publish-target` above and for the same
45
+ * reason: a project token names one. That is what lets a generated repository
46
+ * hold one secret and no variables at all.
47
+ *
48
+ * ## What is deliberately not here
49
+ *
50
+ * No loader URL, no `vendorRev`, no `x-platform-project`. This service holds
51
+ * the operator relationship with the build platform and calls it; a publisher
52
+ * talks to this API and nothing else. The one exception is the presigned
53
+ * upload URL, which points at object storage -- bytes do not travel through
54
+ * here.
55
+ *
56
+ * It is also what makes this publishable by a project token at all. The
57
+ * platform's own verify step publishes a canary to a throwaway project, and a
58
+ * throwaway has no secrets, so the loader has nothing to check a caller
59
+ * against and refuses with a 503 naming a project nobody has heard of. Behind
60
+ * this API the caller is never involved in that exchange.
61
+ */
62
+ "/publish": {
63
+ POST: {
64
+ body: {
65
+ /** The build's own hash. Repeating it returns the same publish. */
66
+ buildHash: string;
67
+ /** Null only for a seed publish. See `publishPlan.ts`. */
68
+ commit: string | null;
69
+ /** The branch content saves commit to. Required when `commit` is set. */
70
+ branch: string | null;
71
+ /** Which dependency layer this was built against, sent or not. */
72
+ layerRev: string | null;
73
+ /**
74
+ * Whether the app links its own CSS.
75
+ *
76
+ * Build metadata the loader needs and no artifact carries, so it has to
77
+ * be declared. Null is a third answer -- "this build did not say" --
78
+ * and is not false.
79
+ */
80
+ linksOwnCss: boolean | null;
81
+ artifacts: {
82
+ /** See `publishPlan.ts` for the namespace. */
83
+ key: string;
84
+ sha256: string;
85
+ bytes: number;
86
+ }[];
87
+ };
88
+ res: {
89
+ publishId: string;
90
+ state: PublishState;
91
+ project: {
92
+ publicProjectId: string;
93
+ /** Null when the project has no site yet -- reported, not refused. */
94
+ siteUrl: string | null;
95
+ };
96
+ /**
97
+ * One per artifact this project does not already hold, and nothing else.
98
+ *
99
+ * So this doubles as the answer to "what is missing": there is no
100
+ * separate field to keep in step with it. Slots expire; a presigned PUT
101
+ * that answers 403 means declare again, not that the publish failed.
102
+ */
103
+ uploads: {
104
+ key: string;
105
+ url: string;
106
+ method: "PUT";
107
+ headers: Record<string, string>;
108
+ /** ISO 8601. */
109
+ expiresAt: string;
110
+ }[];
111
+ /** Keys already held, so a caller can see what it did not have to send. */
112
+ have: string[];
113
+ };
114
+ };
115
+ };
116
+ "/publish/:publishId": {
117
+ GET: {
118
+ res: {
119
+ publishId: string;
120
+ state: PublishState;
121
+ buildHash: string;
122
+ /** Artifact keys still not uploaded. */
123
+ missing: string[];
124
+ problems: PublishProblem[];
125
+ };
126
+ };
127
+ };
128
+ /** Everything asked for has been uploaded. The uploads are checked here. */
129
+ "/publish/:publishId/artifacts": {
130
+ POST: {
131
+ res: {
132
+ state: PublishState;
133
+ problems: PublishProblem[];
134
+ };
135
+ };
136
+ };
137
+ /** A canary build and render, on the build platform, with our credential. */
138
+ "/publish/:publishId/verify": {
139
+ POST: {
140
+ res: {
141
+ state: PublishState;
142
+ ok: boolean;
143
+ /** Where the canary can be looked at, when it rendered. */
144
+ previewUrl: string | null;
145
+ problems: PublishProblem[];
146
+ };
147
+ };
148
+ };
149
+ /**
150
+ * Move the project's pointer to this build.
151
+ *
152
+ * Refused when the commit is no longer the branch head -- which this service
153
+ * is the authority on (see `getGitHead`), so the rule is enforced where the
154
+ * data already is rather than a round trip away.
155
+ */
156
+ "/publish/:publishId/promote": {
157
+ POST: {
158
+ res: {
159
+ state: PublishState;
160
+ url: string | null;
161
+ commit: string | null;
162
+ };
163
+ };
164
+ };
165
+ /** Exchange a personal access token for a short-lived publish token. */
166
+ "/publish-token": {
167
+ POST: {
168
+ res: {
169
+ /** Shown once, here. Nothing can produce it again. */
170
+ token: string;
171
+ /** ISO 8601, because a `Date` does not survive JSON as one. */
172
+ expiresAt: string | null;
173
+ publicProjectId: string;
174
+ productionUrl: string | null;
175
+ };
176
+ };
177
+ };
178
+ };
179
+ /**
180
+ * Where a publish is. Mirrors `PublishState` in `utils/publishPlan.ts`, which
181
+ * owns the transition table; this is the wire spelling of it.
182
+ */
183
+ export type PublishState = "awaiting-artifacts" | "ready" | "verified" | "live" | "failed" | "expired";
184
+ /**
185
+ * Why a publish is not going anywhere.
186
+ *
187
+ * `code` is the stable half -- a pipeline gates on it -- and carries both this
188
+ * API's own codes (`ARTIFACT_MISMATCH`, `POINTER_STALE`, the declaration codes
189
+ * in `publishPlan.ts`) and the build platform's `PLATFORM*` codes passed
190
+ * through from a verify, because rewording those would lose the only sentence
191
+ * that says what to change.
192
+ *
193
+ * `hint` is not decoration. A gate that merely fails is useless in somebody
194
+ * else's pipeline: they get a red build and no idea why.
195
+ */
196
+ export type PublishProblem = {
197
+ code: string;
198
+ message: string;
199
+ hint?: string;
200
+ keys?: string[];
201
+ };
@@ -0,0 +1,123 @@
1
+ import { z } from "zod";
2
+ import { ContentPublishApi, PublishProblem, PublishState } from "./contentApi.js";
3
+ /**
4
+ * Reading what content answers.
5
+ *
6
+ * Every type here comes from `contentApi.ts`, which is a copy of the
7
+ * service's own `Api.ts` - so the shapes are not re-derived from what this end
8
+ * wishes it received, and a copy brought up to date fails to compile wherever
9
+ * this CLI has not caught up.
10
+ *
11
+ * The schemas are the runtime half, and they are needed as well as the types:
12
+ * what arrives is JSON over HTTP from a service that versions separately, so a
13
+ * type alone asserts a shape rather than checking one. Each schema is
14
+ * annotated with the copied type it must produce (`z.ZodType<DeclareResponse>`
15
+ * and so on), which is what ties the two halves together - a schema that drifts
16
+ * from the copy does not compile, and an answer that drifts from the schema
17
+ * does not parse. `ValOpsHttp` in `@valbuild/server` reads every other content
18
+ * route the same way.
19
+ *
20
+ * Nothing here is laxer than the copy. An optional field is optional because
21
+ * the service says so; making a required one optional here would hide exactly
22
+ * the drift this file exists to catch.
23
+ */
24
+ export type { PublishProblem, PublishState };
25
+ export type DeclareBody = ContentPublishApi["/publish"]["POST"]["body"];
26
+ export type DeclareResponse = ContentPublishApi["/publish"]["POST"]["res"];
27
+ export type ArtifactsResponse = ContentPublishApi["/publish/:publishId/artifacts"]["POST"]["res"];
28
+ export type VerifyResponse = ContentPublishApi["/publish/:publishId/verify"]["POST"]["res"];
29
+ export type PromoteResponse = ContentPublishApi["/publish/:publishId/promote"]["POST"]["res"];
30
+ export type StatusResponse = ContentPublishApi["/publish/:publishId"]["GET"]["res"];
31
+ export type PublishTokenResponse = ContentPublishApi["/publish-token"]["POST"]["res"];
32
+ /** An artifact, as it is declared: by key, by hash, by size. */
33
+ export type DeclaredArtifact = DeclareBody["artifacts"][number];
34
+ /** Permission to write one artifact's bytes to object storage. */
35
+ export type UploadSlot = DeclareResponse["uploads"][number];
36
+ /**
37
+ * Content answered, and the answer was not one this CLI can act on.
38
+ *
39
+ * Separate from a refusal, which is content saying no in a sentence meant for
40
+ * a person. This is two programs disagreeing - the copy in `contentApi.ts` and
41
+ * the service that has moved on from it - and the two want opposite things
42
+ * said about them.
43
+ */
44
+ export declare class PublishProtocolError extends Error {
45
+ constructor(message: string);
46
+ }
47
+ export declare const parseDeclare: (body: unknown) => DeclareResponse;
48
+ export declare const parseArtifacts: (body: unknown) => ArtifactsResponse;
49
+ export declare const parseVerify: (body: unknown) => VerifyResponse;
50
+ export declare const parsePromote: (body: unknown) => PromoteResponse;
51
+ export declare const parseStatus: (body: unknown) => StatusResponse;
52
+ export declare const parsePublishToken: (body: unknown, call: string) => PublishTokenResponse;
53
+ /**
54
+ * What a build targets, and the project's files, as the Studio asks for them.
55
+ *
56
+ * Both are read through `/publish-api` rather than fetched from the loader,
57
+ * because a browser has neither the loader's address nor a credential it would
58
+ * accept. `fetchBuildTarget` in `@valbuild/tanstack-build` is the other half --
59
+ * same shape, a different caller, and it CASTS where this parses.
60
+ *
61
+ * Parsed rather than cast here because of what sits in between: a proxy, a
62
+ * gateway and a service boundary. An HTML error page cast to `BuildTarget`
63
+ * fails several layers down inside rolldown with a message about a module
64
+ * specifier, and the publish that follows would label a build against a layer
65
+ * that was never there.
66
+ *
67
+ * The TYPE is inferred from this schema instead of imported. `BuildTarget` is
68
+ * declared in `@valbuild/tanstack-build`, which has no zod and deliberately
69
+ * almost no dependencies -- it goes into a browser bundle -- and this package
70
+ * is imported by everything. The declarations are kept honest at the one place
71
+ * the value is used: it is passed straight into `buildUserApp({ target })`, so
72
+ * a drift between them is a compile error at that call rather than a runtime
73
+ * surprise.
74
+ */
75
+ declare const buildTargetResponse: z.ZodObject<{
76
+ base: z.ZodObject<{
77
+ rev: z.ZodString;
78
+ shellRev: z.ZodString;
79
+ rscShellRev: z.ZodString;
80
+ modules: z.ZodRecord<z.ZodString, z.ZodString>;
81
+ rscModules: z.ZodRecord<z.ZodString, z.ZodString>;
82
+ paths: z.ZodObject<{
83
+ baseFromProject: z.ZodString;
84
+ projectVendorDir: z.ZodString;
85
+ rscVendorBase: z.ZodString;
86
+ rscRuntimeSpecifier: z.ZodString;
87
+ rscRuntimePath: z.ZodString;
88
+ flightServer: z.ZodString;
89
+ serverFnBase: z.ZodString;
90
+ }, z.core.$strip>;
91
+ }, z.core.$strip>;
92
+ project: z.ZodObject<{
93
+ rev: z.ZodNullable<z.ZodString>;
94
+ rsc: z.ZodBoolean;
95
+ modules: z.ZodRecord<z.ZodString, z.ZodString>;
96
+ css: z.ZodRecord<z.ZodString, z.ZodString>;
97
+ workerOnly: z.ZodArray<z.ZodString>;
98
+ }, z.core.$strip>;
99
+ }, z.core.$strip>;
100
+ export type BuildTargetResponse = z.infer<typeof buildTargetResponse>;
101
+ /**
102
+ * `null` files is a real answer: a project that has never published.
103
+ *
104
+ * Distinguished from an empty record, which would be a project that published
105
+ * nothing -- and which a build would take as "no entry point" rather than as
106
+ * "there is nothing here yet".
107
+ */
108
+ declare const projectSourceResponse: z.ZodObject<{
109
+ files: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>;
110
+ }, z.core.$strip>;
111
+ export type ProjectSourceResponse = z.infer<typeof projectSourceResponse>;
112
+ export declare const parseBuildTarget: (body: unknown) => BuildTargetResponse;
113
+ export declare const parseProjectSource: (body: unknown) => ProjectSourceResponse;
114
+ /**
115
+ * The `details` of a refusal, when it carries a list of problems.
116
+ *
117
+ * Every non-2xx is `{ statusCode, message, details? }`, and the publish routes
118
+ * put `PublishProblem[]` in `details` - a declaration is answered with EVERY
119
+ * problem rather than the first, because finding them one round trip at a time
120
+ * is how a publish takes six attempts. Anything else in there is somebody
121
+ * else's `details` and is left to the caller's own message.
122
+ */
123
+ export declare function parseProblems(details: unknown): PublishProblem[];
@@ -0,0 +1,47 @@
1
+ /**
2
+ * One value, computed once per REQUEST.
3
+ *
4
+ * `fetchVal` in draft mode reads the whole module tree out of `/sources/~`, and
5
+ * a page that calls it three times used to ask three times — the answer being
6
+ * byte-identical each time, because the query and the session are fixed for the
7
+ * length of a request. This is what makes the second and third call free.
8
+ *
9
+ * ## Why the scope is the REQUEST and can never be wider
10
+ *
11
+ * The memoised response is `own_patch_groups_only: true` — it contains the
12
+ * CALLER'S OWN unpublished work, resolved from their session cookie. A memo
13
+ * that outlived the request would serve one author's staged edits to the next
14
+ * visitor, which is the exact failure `own_patch_groups_only` exists to
15
+ * prevent, and it would be invisible: the draft would simply render somebody
16
+ * else's half-finished sentence.
17
+ *
18
+ * So this module holds NO state of its own. The caller supplies the box, and
19
+ * the box has to come from something the framework already scopes to the
20
+ * request — React's `cache()` in an RSC, a `WeakMap` keyed on the `Request` in
21
+ * TanStack Start. `null` means "no request scope available", and that is not an
22
+ * error: it falls back to computing every time, which is what the code did
23
+ * before this existed. Ineffective is a safe failure here; leaking is not.
24
+ *
25
+ * The `key` is the second half of that guarantee. It carries whatever the
26
+ * answer depends on — the session — so a box that somehow outlived its request,
27
+ * or is shared by two sessions, MISSES rather than serving the wrong draft.
28
+ *
29
+ * ## The promise is cached, not the value
30
+ *
31
+ * React renders siblings concurrently, so three `fetchVal` calls in one page
32
+ * are usually in flight at the same time rather than one after another.
33
+ * Caching the resolved value would dedupe none of them; caching the promise
34
+ * dedupes both orderings.
35
+ */
36
+ export type RequestScopedMemo<T> = {
37
+ key?: string;
38
+ promise?: Promise<T>;
39
+ };
40
+ /**
41
+ * Returns the memoised promise for `key`, computing it only on a miss.
42
+ *
43
+ * A rejection is memoised along with everything else, on purpose: one failed
44
+ * read per request is the same answer for every caller in it, and retrying it
45
+ * two more times only makes a broken draft slower to render.
46
+ */
47
+ export declare function memoizePerRequest<T>(box: RequestScopedMemo<T> | null, key: string, compute: () => Promise<T>): Promise<T>;
@@ -488,6 +488,7 @@ exports._arrayLikeToArray = _arrayLikeToArray;
488
488
  exports._createForOfIteratorHelper = _createForOfIteratorHelper;
489
489
  exports._defineProperty = _defineProperty;
490
490
  exports._objectSpread2 = _objectSpread2;
491
+ exports._toPropertyKey = _toPropertyKey;
491
492
  exports._typeof = _typeof;
492
493
  exports._unsupportedIterableToArray = _unsupportedIterableToArray;
493
494
  exports.getNextAppRouterSourceFolder = getNextAppRouterSourceFolder;
@@ -475,4 +475,4 @@ function getPatternFromModuleFilePath(moduleFilePath, srcFolder) {
475
475
  .replace("/page.val.ts", "").replace("/page.val.js", "");
476
476
  }
477
477
 
478
- export { DEFAULT_CANVAS_SELECTION as D, VAL_CONFIG_SESSION_STORAGE_KEY as V, _createForOfIteratorHelper as _, VAL_THEME_SESSION_STORAGE_KEY as a, VAL_AI_SESSION_STORAGE_KEY as b, VAL_CANVAS_PARAM as c, VAL_CANVAS_MESSAGE as d, DEFAULT_CANVAS_SELECTION_SOFT as e, isValCanvasStudioMessage as f, isValCanvasFrame as g, getNextAppRouterSourceFolder as h, isValCanvasPageMessage as i, getTanStackRouterSourceFolder as j, getPageRouterSourceFolder as k, isTanStackRoutesFolder as l, getPageRouterSitemapTree as m, getPatternFromModuleFilePath as n, _objectSpread2 as o, parseRoutePattern as p, _arrayLikeToArray as q, _unsupportedIterableToArray as r, _typeof as s, _defineProperty as t, withValCanvasParam as w };
478
+ export { DEFAULT_CANVAS_SELECTION as D, VAL_CONFIG_SESSION_STORAGE_KEY as V, _createForOfIteratorHelper as _, VAL_THEME_SESSION_STORAGE_KEY as a, VAL_AI_SESSION_STORAGE_KEY as b, VAL_CANVAS_PARAM as c, VAL_CANVAS_MESSAGE as d, DEFAULT_CANVAS_SELECTION_SOFT as e, isValCanvasStudioMessage as f, isValCanvasFrame as g, getNextAppRouterSourceFolder as h, isValCanvasPageMessage as i, getTanStackRouterSourceFolder as j, getPageRouterSourceFolder as k, isTanStackRoutesFolder as l, getPageRouterSitemapTree as m, getPatternFromModuleFilePath as n, _objectSpread2 as o, parseRoutePattern as p, _arrayLikeToArray as q, _unsupportedIterableToArray as r, _typeof as s, _defineProperty as t, _toPropertyKey as u, withValCanvasParam as w };
@@ -488,6 +488,7 @@ exports._arrayLikeToArray = _arrayLikeToArray;
488
488
  exports._createForOfIteratorHelper = _createForOfIteratorHelper;
489
489
  exports._defineProperty = _defineProperty;
490
490
  exports._objectSpread2 = _objectSpread2;
491
+ exports._toPropertyKey = _toPropertyKey;
491
492
  exports._typeof = _typeof;
492
493
  exports._unsupportedIterableToArray = _unsupportedIterableToArray;
493
494
  exports.getNextAppRouterSourceFolder = getNextAppRouterSourceFolder;