@valbuild/server 0.135.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 +108 -0
- package/dist/declarations/src/ValOps.d.ts +41 -0
- package/dist/declarations/src/ValOpsHttp.d.ts +76 -0
- package/dist/declarations/src/index.d.ts +1 -1
- package/dist/declarations/src/loadValModules.d.ts +23 -0
- package/dist/valbuild-server.cjs.dev.js +362 -7
- package/dist/valbuild-server.cjs.prod.js +362 -7
- package/dist/valbuild-server.esm.js +362 -8
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,113 @@
|
|
|
1
1
|
# @valbuild/server
|
|
2
2
|
|
|
3
|
+
## 0.136.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#716](https://github.com/valbuild/val/pull/716) [`4450be5`](https://github.com/valbuild/val/commit/4450be570ec3a66d7d98ba273c333ca2d2f163d9) Thanks [@freekh](https://github.com/freekh)! - `rebakeGit` rewires a project record to a different commit, and the Studio's publish proxy can now reach `/project-source`.
|
|
8
|
+
|
|
9
|
+
Both are groundwork for a publish that happens in the browser. `rebakeGit` replaces the one line in the generated `val.server.ts` that carries the commit, rather than regenerating the file: a publisher running inside the deployment does not know the project id or the platform's address, and reconstructing the file from what it could guess would throw away wiring it cannot see.
|
|
10
|
+
|
|
11
|
+
- [#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.
|
|
12
|
+
|
|
13
|
+
- **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.
|
|
14
|
+
- **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.
|
|
15
|
+
- **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.
|
|
16
|
+
- **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.
|
|
17
|
+
- 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.
|
|
18
|
+
- `@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.
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [[`cf89e74`](https://github.com/valbuild/val/commit/cf89e7429a79c4304ecbedd4f8b571a0de0f145f), [`c219574`](https://github.com/valbuild/val/commit/c21957498f3c7f4f47cef197c5dc0d591aa744ca), [`e673b43`](https://github.com/valbuild/val/commit/e673b43feaf48b7f30881d6786c858a0cb6a44a2), [`5c82928`](https://github.com/valbuild/val/commit/5c82928992d29f133f81cf0704a273c0449b18f1), [`d2f385a`](https://github.com/valbuild/val/commit/d2f385a04978992a5036379235e447ba7775faef)]:
|
|
21
|
+
- @valbuild/ui@0.136.1
|
|
22
|
+
- @valbuild/shared@0.136.1
|
|
23
|
+
- @valbuild/core@0.136.1
|
|
24
|
+
|
|
25
|
+
## 0.136.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- [#713](https://github.com/valbuild/val/pull/713) [`f3a4bb7`](https://github.com/valbuild/val/commit/f3a4bb7aea604943b92545c122243798c759715c) Thanks [@freekh](https://github.com/freekh)! - The Studio can load a bundler, and it stops telling a managed project that its
|
|
30
|
+
publish is on its way out.
|
|
31
|
+
|
|
32
|
+
**A managed project is one with no repository and no host watching one.** There
|
|
33
|
+
is nothing outside the browser to pick a commit up, so the Studio has been
|
|
34
|
+
showing it a story that belongs to a project with a repository: a `Building`
|
|
35
|
+
spinner, waiting for an event that can never arrive. It does not resolve on a
|
|
36
|
+
reload, on a retry, or tomorrow, and there is no way to tell it from a deploy
|
|
37
|
+
that is merely slow.
|
|
38
|
+
|
|
39
|
+
The content service now reports which kind of project this is, as `sourceMode`
|
|
40
|
+
on `/stat`, and the Studio narrates accordingly:
|
|
41
|
+
|
|
42
|
+
- a **connected** project keeps the deploy feed and keeps `Building`, because
|
|
43
|
+
there a host genuinely does pick the commit up;
|
|
44
|
+
- a **managed** one says `Saved, not yet live` instead — a durable condition,
|
|
45
|
+
not a phase, because nothing else will ever resolve it.
|
|
46
|
+
|
|
47
|
+
A server that reports no source mode keeps the behaviour it has today. Absence
|
|
48
|
+
means "not reported", never "managed": `fs` mode has no project to have a mode,
|
|
49
|
+
and neither does a content service that predates the field.
|
|
50
|
+
|
|
51
|
+
**The Studio also loads `@valbuild/tanstack-build` in the tab**, at mount and at
|
|
52
|
+
idle, for the managed projects that will need it — a browser that is the
|
|
53
|
+
deployer needs a bundler. This is the groundwork for browser-side publishing;
|
|
54
|
+
the publish path itself is unchanged in this release.
|
|
55
|
+
|
|
56
|
+
That bundler is `@rolldown/browser`, whose WebAssembly binary is 10.9 MB and is
|
|
57
|
+
**not** in the npm package. It is served from `static.val.build`, addressed by
|
|
58
|
+
the SHA-256 of its own bytes, so the build and the binary it needs cannot drift
|
|
59
|
+
apart. A deployment that must not reach that host — an air-gapped install, a
|
|
60
|
+
mirror — sets `globalThis.__VAL_ROLLDOWN_WASM_URL__` before the Studio loads and
|
|
61
|
+
needs no rebuild.
|
|
62
|
+
|
|
63
|
+
**Building in the browser requires a cross-origin isolated page.** Rolldown runs
|
|
64
|
+
WebAssembly on worker threads that share memory, and a browser will not hand a
|
|
65
|
+
`SharedArrayBuffer` to a worker otherwise. Without
|
|
66
|
+
`Cross-Origin-Opener-Policy: same-origin` and
|
|
67
|
+
`Cross-Origin-Embedder-Policy: require-corp` on the document Val is mounted in,
|
|
68
|
+
loading the bundler now fails with a message that says exactly that, instead of
|
|
69
|
+
a `DataCloneError` thrown from inside a worker. Nothing else in this release is
|
|
70
|
+
affected: a Studio that never builds in the browser never asks.
|
|
71
|
+
|
|
72
|
+
### Patch Changes
|
|
73
|
+
|
|
74
|
+
- [#699](https://github.com/valbuild/val/pull/699) [`1d72d00`](https://github.com/valbuild/val/commit/1d72d00a1b036959ae0f1540121701cbb2694dcb) Thanks [@freekh](https://github.com/freekh)! - A `*.val.ts` with no default export is no longer reported as a missing module
|
|
75
|
+
|
|
76
|
+
`*.val.ts` is a naming convention, not a promise: plenty of files under it hold
|
|
77
|
+
only the schemas and helpers the modules beside them import. Every one of those
|
|
78
|
+
was getting two errors in the editor — `Module '…' was not found in
|
|
79
|
+
val.modules` and `… is not registered in val.modules, so Val will not serve it`
|
|
80
|
+
— both on line 1, telling their author to register a file that has nothing to
|
|
81
|
+
register. `val validate` has never reported them; only the editor did.
|
|
82
|
+
|
|
83
|
+
The default export is what makes a `*.val.ts` a module, so that is what the
|
|
84
|
+
diagnostic now asks about:
|
|
85
|
+
|
|
86
|
+
- **No default export → nothing is reported.** The file is not a module, so it
|
|
87
|
+
is not a module Val is failing to serve.
|
|
88
|
+
- **A default export → one diagnostic, on the `export default` itself** rather
|
|
89
|
+
than on line 1, so it is next to the thing that has to change, and the
|
|
90
|
+
duplicate fatal beside it is gone. The message now gives both remedies: add
|
|
91
|
+
the file to `val.modules`, or export what it holds by name instead. The "Val:
|
|
92
|
+
register … in val.modules" quick fix is offered there.
|
|
93
|
+
|
|
94
|
+
The rule is `findDefaultExport` in `@valbuild/server`, which `val validate`
|
|
95
|
+
already used to decide the same question — so the editor and the CLI now agree
|
|
96
|
+
about which files are modules, including the cases that are easy to get wrong
|
|
97
|
+
(`export * from …` carries no default; `export type { T as default }` and
|
|
98
|
+
`export default interface T {}` are both gone after transpilation).
|
|
99
|
+
|
|
100
|
+
Because the diagnostic now replaces a module's own findings rather than adding
|
|
101
|
+
to them, the editor also stops guessing about registration it cannot read: a
|
|
102
|
+
`val.modules` that registers modules through a tsconfig path alias
|
|
103
|
+
(`import("_/content/page.val")`), or that builds its list in another file, is no
|
|
104
|
+
longer taken to register nothing.
|
|
105
|
+
|
|
106
|
+
- Updated dependencies [[`f3a4bb7`](https://github.com/valbuild/val/commit/f3a4bb7aea604943b92545c122243798c759715c)]:
|
|
107
|
+
- @valbuild/ui@0.136.0
|
|
108
|
+
- @valbuild/core@0.136.0
|
|
109
|
+
- @valbuild/shared@0.136.0
|
|
110
|
+
|
|
3
111
|
## 0.135.0
|
|
4
112
|
|
|
5
113
|
### Patch Changes
|
|
@@ -417,6 +417,47 @@ export declare abstract class ValOps {
|
|
|
417
417
|
* project requires.
|
|
418
418
|
*/
|
|
419
419
|
publishRefusal(): PublishRefusal | null;
|
|
420
|
+
/**
|
|
421
|
+
* How this project's SOURCE is kept, or `null` when it is nobody's question.
|
|
422
|
+
*
|
|
423
|
+
* `"managed"` -- the content service is the store of record, there is no
|
|
424
|
+
* repository, and nothing outside the browser will ever pick a commit up.
|
|
425
|
+
* `"connected"` -- commits are mirrored into a repository a host watches.
|
|
426
|
+
*
|
|
427
|
+
* `null` is the honest answer for `fs` and memory mode, where publishing is
|
|
428
|
+
* writing to disk and there is no project to have a mode, and for an `http`
|
|
429
|
+
* project whose content service predates the field. The Studio treats it as
|
|
430
|
+
* "the story I have always told", which is the connected one -- the feed, and
|
|
431
|
+
* a `building` state that something outside resolves. Guessing `managed`
|
|
432
|
+
* instead would take the deploy feed away from every project running against
|
|
433
|
+
* an older service.
|
|
434
|
+
*/
|
|
435
|
+
sourceMode(): "managed" | "connected" | null;
|
|
436
|
+
/**
|
|
437
|
+
* Forward one call to the content service's publish API, as this project.
|
|
438
|
+
*
|
|
439
|
+
* The Studio builds a managed project in the tab and then has to publish what
|
|
440
|
+
* it built -- but a browser cannot talk to content directly. It holds a
|
|
441
|
+
* session cookie for THIS origin and no credential content would accept, so
|
|
442
|
+
* the conversation goes through here, exactly as patches already do.
|
|
443
|
+
*
|
|
444
|
+
* Refused by default, and that is the honest answer rather than a gap: `fs`
|
|
445
|
+
* and memory mode have no content service to forward to, and publishing there
|
|
446
|
+
* is writing to disk.
|
|
447
|
+
*
|
|
448
|
+
* The path is content's, minus the `/v1` prefix -- `/publish`,
|
|
449
|
+
* `/publish/{id}/artifacts`, `/build-target`. The ALLOW LIST lives in the
|
|
450
|
+
* implementation rather than here, because it is what keeps this from being
|
|
451
|
+
* a way to reach the rest of content with the project's credential.
|
|
452
|
+
*/
|
|
453
|
+
publishApi(_path: string, _init: {
|
|
454
|
+
method: string;
|
|
455
|
+
body?: string;
|
|
456
|
+
}): Promise<{
|
|
457
|
+
status: number;
|
|
458
|
+
body: string;
|
|
459
|
+
contentType: string;
|
|
460
|
+
}>;
|
|
420
461
|
/**
|
|
421
462
|
* Whether a commit here produces `.val.ts` TEXT as well as data.
|
|
422
463
|
*
|
|
@@ -126,6 +126,82 @@ export declare class ValOpsHttp extends ValOps {
|
|
|
126
126
|
* checking.
|
|
127
127
|
*/
|
|
128
128
|
publishRefusal(): PublishRefusal | null;
|
|
129
|
+
/**
|
|
130
|
+
* What the content service last said this project's source mode is.
|
|
131
|
+
*
|
|
132
|
+
* Read off the same remembered expectation {@link publishRefusal} uses. It is
|
|
133
|
+
* CURRENT wherever it matters rather than a poll behind, and by construction
|
|
134
|
+
* rather than by luck: the only caller is `/stat`, which awaits `getStat`
|
|
135
|
+
* first, and that fetches the patches -- which is the response the expectation
|
|
136
|
+
* is recorded from.
|
|
137
|
+
*
|
|
138
|
+
* `null` before anything has fetched patches, which is the same "not
|
|
139
|
+
* reported" the wire field means, and reads as connected. The alternative
|
|
140
|
+
* would be to ask for it separately, which is a round trip for a field that
|
|
141
|
+
* has just arrived.
|
|
142
|
+
*/
|
|
143
|
+
sourceMode(): "managed" | "connected" | null;
|
|
144
|
+
/**
|
|
145
|
+
* The short-lived publish token, and when it stops being usable.
|
|
146
|
+
*
|
|
147
|
+
* `null` until something publishes, which for most deployments is never.
|
|
148
|
+
*/
|
|
149
|
+
private publishToken;
|
|
150
|
+
/**
|
|
151
|
+
* Trade this deployment's credential for one that can do one thing.
|
|
152
|
+
*
|
|
153
|
+
* Content's publish API takes a PROJECT TOKEN and nothing else --
|
|
154
|
+
* `authenticateProjectToken` refuses anything that is not one, deliberately,
|
|
155
|
+
* because a personal access token is a person's credential and does not name
|
|
156
|
+
* a project. What this deployment holds is the project's api key, which is
|
|
157
|
+
* neither.
|
|
158
|
+
*
|
|
159
|
+
* `POST /v1/{org}/{project}/publish-token` is the exchange, and it was built
|
|
160
|
+
* for exactly this: its own docblock names the case where "the caller was the
|
|
161
|
+
* project's api key ... a machine exchanging one machine credential for a
|
|
162
|
+
* narrower one". What comes back can publish one project for ten minutes.
|
|
163
|
+
*
|
|
164
|
+
* So the api key never leaves this process and the browser never sees any
|
|
165
|
+
* credential at all. That is the point of routing the publish through here
|
|
166
|
+
* rather than letting the tab talk to content.
|
|
167
|
+
*/
|
|
168
|
+
private mintPublishToken;
|
|
169
|
+
/**
|
|
170
|
+
* A usable publish token, minting one when what we have will not last.
|
|
171
|
+
*
|
|
172
|
+
* The margin is what stops a token that is valid when the publish starts from
|
|
173
|
+
* expiring in the middle of it: a publish is five calls and an upload of
|
|
174
|
+
* every artifact, and the upload is the slow one.
|
|
175
|
+
*/
|
|
176
|
+
private currentPublishToken;
|
|
177
|
+
/**
|
|
178
|
+
* The content paths this may reach, and nothing else.
|
|
179
|
+
*
|
|
180
|
+
* An allow list rather than a prefix check, because this method holds a
|
|
181
|
+
* credential and the browser chooses the path. `/publish/{id}` and its three
|
|
182
|
+
* steps are the publish conversation; `/build-target` is what a build needs
|
|
183
|
+
* to know before it starts; `/project-source` is what it builds.
|
|
184
|
+
*
|
|
185
|
+
* `/project-source` is here rather than on a route of its own because it is
|
|
186
|
+
* one of the three things a publish asks for and none of them are useful
|
|
187
|
+
* apart -- and because the credential is the same one. The Studio cannot get
|
|
188
|
+
* the project's files any other way: it runs inside the deployment, which
|
|
189
|
+
* holds a session for its own origin and an api key for content, and content
|
|
190
|
+
* is the only thing it is allowed to talk to at all.
|
|
191
|
+
*
|
|
192
|
+
* A publish id is opaque and content-generated, so it is matched rather than
|
|
193
|
+
* parsed -- what matters is that nothing with a `..`, a query or another
|
|
194
|
+
* segment gets through.
|
|
195
|
+
*/
|
|
196
|
+
private static publishApiPathAllowed;
|
|
197
|
+
publishApi(path: string, init: {
|
|
198
|
+
method: string;
|
|
199
|
+
body?: string;
|
|
200
|
+
}): Promise<{
|
|
201
|
+
status: number;
|
|
202
|
+
body: string;
|
|
203
|
+
contentType: string;
|
|
204
|
+
}>;
|
|
129
205
|
onInit(): Promise<void>;
|
|
130
206
|
getPresignedAuthNonce(profileId: string, corsOrigin: string): Promise<{
|
|
131
207
|
status: "success";
|
|
@@ -44,7 +44,7 @@ export type { ModulePathMap } from "./modulePathMap.js";
|
|
|
44
44
|
export { ValOpsFS } from "./ValOpsFS.js";
|
|
45
45
|
export { ValOpsHttp } from "./ValOpsHttp.js";
|
|
46
46
|
export { ValOpsMemory, InMemoryPatchStore, type ValPatchStore, type StoredPatch, type ValOpsMemoryOptions, } from "./ValOpsMemory.js";
|
|
47
|
-
export { loadValModules, createValModuleFileInspector } from "./loadValModules.js";
|
|
47
|
+
export { loadValModules, createValModuleFileInspector, findDefaultExport, } from "./loadValModules.js";
|
|
48
48
|
export type { ValModuleFileInspection } from "./loadValModules.js";
|
|
49
49
|
export { formatPatchSourceError } from "./ValOps.js";
|
|
50
50
|
export { compareWithCapturedReport, readCapturedReport, replaySnapshot, } from "./debug/replaySnapshot.js";
|
|
@@ -71,3 +71,26 @@ export type ValModuleFileInspection =
|
|
|
71
71
|
* project's own first-party files.
|
|
72
72
|
*/
|
|
73
73
|
export declare function createValModuleFileInspector(projectRoot: string, host?: ValModulesHost): (absPath: string) => ValModuleFileInspection;
|
|
74
|
+
/**
|
|
75
|
+
* The statement that exports a RUNTIME value as `default`, without evaluating
|
|
76
|
+
* it — or `undefined` when the file has no such export.
|
|
77
|
+
*
|
|
78
|
+
* This is the one rule that separates a Val module from a `*.val.ts` that
|
|
79
|
+
* merely wears the naming convention: a shared schema or helper is exported by
|
|
80
|
+
* name, a module is exported by default. Every caller that needs to tell those
|
|
81
|
+
* apart goes through this — `val validate` to decide whether an unregistered
|
|
82
|
+
* file is worth reporting, and the language server to decide the same thing and
|
|
83
|
+
* to put its diagnostic on the export rather than on line 1.
|
|
84
|
+
*
|
|
85
|
+
* Two things deliberately do not count, because neither exists once the file is
|
|
86
|
+
* transpiled — and treating either as a default export would send a pure helper
|
|
87
|
+
* off to be evaluated and reported:
|
|
88
|
+
*
|
|
89
|
+
* - `export * from "./x"`, since a star re-export never carries the default;
|
|
90
|
+
* - a type-only export, in any of its three spellings
|
|
91
|
+
* (`export type { T as default }`, `export { type T as default }` and
|
|
92
|
+
* `export default interface T {}` — the last one parses as a declaration
|
|
93
|
+
* carrying a `default` modifier, exactly like `export default class`, and is
|
|
94
|
+
* the one that looks like a runtime export and is not).
|
|
95
|
+
*/
|
|
96
|
+
export declare function findDefaultExport(sourceFile: ts.SourceFile): ts.Statement | undefined;
|