@valbuild/server 0.146.4 → 0.148.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.
- package/CHANGELOG.md +129 -0
- package/browser/dist/valbuild-server-browser.cjs.d.ts +2 -0
- package/browser/dist/valbuild-server-browser.cjs.dev.js +146 -0
- package/browser/dist/valbuild-server-browser.cjs.js +7 -0
- package/browser/dist/valbuild-server-browser.cjs.prod.js +146 -0
- package/browser/dist/valbuild-server-browser.esm.js +142 -0
- package/browser/package.json +4 -0
- package/dist/ValRouterCore-42772c21.cjs.prod.js +10824 -0
- package/dist/ValRouterCore-7ca3dc68.esm.js +10784 -0
- package/dist/ValRouterCore-9bdaaf89.cjs.dev.js +10824 -0
- package/dist/declarations/src/ValOps.d.ts +44 -11
- package/dist/declarations/src/ValOpsFS.d.ts +1 -0
- package/dist/declarations/src/ValOpsHttp.d.ts +1 -0
- package/dist/declarations/src/ValOpsMemory.d.ts +13 -4
- package/dist/declarations/src/ValRouter.d.ts +1 -1
- package/dist/declarations/src/ValServer.d.ts +9 -3
- package/dist/declarations/src/browser/inTabFetch.d.ts +27 -0
- package/dist/declarations/src/browser/index.d.ts +9 -0
- package/dist/declarations/src/checkRemoteRef.d.ts +40 -2
- package/dist/declarations/src/createFixPatch.d.ts +25 -3
- package/dist/declarations/src/fixFiles.d.ts +42 -0
- package/dist/declarations/src/fixHandlers.d.ts +15 -1
- package/dist/declarations/src/getSettings.d.ts +1 -0
- package/dist/declarations/src/index.d.ts +1 -0
- package/dist/declarations/src/linkCheck/probeUrl.d.ts +40 -0
- package/dist/declarations/src/platform.d.ts +55 -0
- package/dist/declarations/src/remoteFileCheck.d.ts +116 -0
- package/dist/declarations/src/remoteHostCheck.d.ts +101 -0
- package/dist/declarations/src/studioValidation.d.ts +84 -0
- package/dist/declarations/src/uploadRemoteFile.d.ts +1 -1
- package/dist/declarations/src/videosetFixes.d.ts +3 -2
- package/dist/getJsonEntryAtCommit-2e36e805.cjs.prod.js +142 -0
- package/dist/getJsonEntryAtCommit-40b6d9c1.esm.js +135 -0
- package/dist/getJsonEntryAtCommit-64f67a50.cjs.dev.js +142 -0
- package/dist/jsonValuesModule-427d68c8.esm.js +273 -0
- package/dist/jsonValuesModule-8ae503a8.cjs.dev.js +288 -0
- package/dist/jsonValuesModule-d1bb8d8f.cjs.prod.js +288 -0
- package/dist/ops-0d05feaf.esm.js +353 -0
- package/dist/ops-b15239ac.cjs.prod.js +361 -0
- package/dist/ops-f37d18c6.cjs.dev.js +361 -0
- package/dist/publishKit-580f558a.cjs.prod.js +41 -0
- package/dist/publishKit-5865095c.cjs.dev.js +41 -0
- package/dist/publishKit-bc8df53d.esm.js +31 -0
- package/dist/valbuild-server.cjs.dev.js +9239 -18895
- package/dist/valbuild-server.cjs.prod.js +9239 -18895
- package/dist/valbuild-server.esm.js +9107 -18763
- package/package.json +17 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,134 @@
|
|
|
1
1
|
# @valbuild/server
|
|
2
2
|
|
|
3
|
+
## 0.148.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#885](https://github.com/valbuild/val/pull/885) [`df5544c`](https://github.com/valbuild/val/commit/df5544c6e92a4c3a52765630c9de191e854ee723) Thanks [@freekh](https://github.com/freekh)! - Demo mode (`<ValApp demo>`) now starts under `vite dev`. It used to stop at
|
|
8
|
+
"The demo could not start: part of it did not load", with
|
|
9
|
+
`does not provide an export named 'Buffer'` in the console, because the server
|
|
10
|
+
that runs in the tab imported the CommonJS `buffer` package, and Vite's dev
|
|
11
|
+
server does not convert a dependency it only meets in a chunk loaded on demand.
|
|
12
|
+
That server now handles bytes without `buffer`, so an app needs no Vite
|
|
13
|
+
configuration for it, and `@valbuild/server` no longer depends on the package.
|
|
14
|
+
If you added `buffer` to `optimizeDeps.include` to work around this, you can
|
|
15
|
+
remove it.
|
|
16
|
+
|
|
17
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - `createFixPatch` reads a file's bytes, and saves a downloaded remote file,
|
|
18
|
+
through an optional `files` reader instead of directly from disk. The default
|
|
19
|
+
reads the disk under `projectRoot` exactly as before, so `val validate --fix`
|
|
20
|
+
and the VS Code extension behave the same.
|
|
21
|
+
|
|
22
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - Remote files are now addressed on the host the project's settings name. For a
|
|
23
|
+
project hosted by Val, that is the project's own address, and its remote images
|
|
24
|
+
and files are served from there rather than from `remote.val.build`. Every other
|
|
25
|
+
project is unchanged.
|
|
26
|
+
|
|
27
|
+
- The Studio, `val validate --fix` and the MCP image tool all take the host from
|
|
28
|
+
the project's settings, falling back to `remote.val.build` as before.
|
|
29
|
+
- Re-reading a remote file whose recorded size or type is out of date keeps it
|
|
30
|
+
on the host it was on.
|
|
31
|
+
- When a remote file's host cannot be reached, tooling downloads it from the
|
|
32
|
+
project's permanent `<id>.valstart.dev` address instead, and keeps it only if
|
|
33
|
+
its bytes match the file the address names.
|
|
34
|
+
|
|
35
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - Remote files served from a project's own site (addresses like
|
|
36
|
+
`https://www.acme.no/_val/file/…`) are now checked for whether that address
|
|
37
|
+
still serves the project. When a custom domain has been removed or moved to
|
|
38
|
+
another project, the image or file is reported, and **Fix** on the Studio's
|
|
39
|
+
Validate page — or `val validate --fix` — points it at the project's permanent
|
|
40
|
+
`<id>.valstart.dev` address. Nothing is uploaded or downloaded: only the address
|
|
41
|
+
changes.
|
|
42
|
+
|
|
43
|
+
Remote file addresses with a `/_val` prefix after the host are now recognised.
|
|
44
|
+
|
|
45
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - **Check everything** on the Studio's Validate page now checks media directories
|
|
46
|
+
(`s.imagedir()`, `s.filedir()`, `s.fontdir()` and `s.videodir()`) by the files
|
|
47
|
+
they list:
|
|
48
|
+
|
|
49
|
+
- An entry whose file is gone has a **Fix** that removes it from the directory.
|
|
50
|
+
- An image or file entry whose recorded size or type does not match its file has
|
|
51
|
+
a **Fix** that corrects it from the file, keeping its alt text.
|
|
52
|
+
- A video entry whose file is gone is reported, and is removed by hand: its
|
|
53
|
+
posters and captions may still be in use.
|
|
54
|
+
- A remote entry is checked with its host, like any remote file.
|
|
55
|
+
|
|
56
|
+
`val validate --fix` fixes media directories exactly as before.
|
|
57
|
+
|
|
58
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - The Studio has a **Validate** page, at `/val/validate`, and a button for it in
|
|
59
|
+
the top bar with the number of errors left.
|
|
60
|
+
|
|
61
|
+
It lists the errors the Studio already shows, as the errors page did, and adds
|
|
62
|
+
**Check everything**: a check of every module on the server, including whether
|
|
63
|
+
each image's and file's recorded size and type match the file itself. It runs
|
|
64
|
+
only when you press it, can be stopped, and its results stay for the session.
|
|
65
|
+
|
|
66
|
+
Where the Studio can make the fix itself — a missing or wrong width, height or
|
|
67
|
+
mime type on an image, file or video — the row has a **Fix** button. A fix is an
|
|
68
|
+
ordinary change: it shows up in Review, is published with everything else, and
|
|
69
|
+
can be undone from the row. Fixes that need a terminal show the
|
|
70
|
+
`val validate --fix` command to copy.
|
|
71
|
+
|
|
72
|
+
`/val/errors` still works and opens the same page.
|
|
73
|
+
|
|
74
|
+
Also fixed: `.jsonValues()` entries no longer report every image in them as
|
|
75
|
+
"not checked against the file" when validated on the server.
|
|
76
|
+
|
|
77
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - **Check everything** on the Studio's Validate page now checks remote images and
|
|
78
|
+
files too.
|
|
79
|
+
|
|
80
|
+
- A remote image or file whose address no longer matches what is recorded about
|
|
81
|
+
it (its size, type or the schema's options changed since it was uploaded) has
|
|
82
|
+
a **Fix**, which re-reads the file and corrects it, as `val validate --fix`
|
|
83
|
+
does.
|
|
84
|
+
- A remote host that does not answer is shown once, at the top of the page, with
|
|
85
|
+
how many files it holds and a **Try again**, rather than as an error on every
|
|
86
|
+
image.
|
|
87
|
+
- A file its host no longer has is shown at the image or file that uses it.
|
|
88
|
+
|
|
89
|
+
Images and files in your unpublished changes are not asked about until they are
|
|
90
|
+
published; the check makes sure the change still has them.
|
|
91
|
+
|
|
92
|
+
- [#818](https://github.com/valbuild/val/pull/818) [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1) Thanks [@freekh](https://github.com/freekh)! - Two new Studio API routes for the full content check, not yet used by the
|
|
93
|
+
Studio's UI:
|
|
94
|
+
|
|
95
|
+
- `POST /api/val/validate` checks one module — its content against its schema,
|
|
96
|
+
and its local images and files against their bytes — on the content the
|
|
97
|
+
Studio is showing.
|
|
98
|
+
- `POST /api/val/validate/fix` returns the patch that fixes one of those errors,
|
|
99
|
+
for the Studio to add as a pending change. It builds only the fixes that need
|
|
100
|
+
no disk or remote host: image, file and video metadata, and a view pointing at
|
|
101
|
+
the wrong module.
|
|
102
|
+
|
|
103
|
+
Also fixed: an API route whose name started with another route's name could be
|
|
104
|
+
answered by the other route.
|
|
105
|
+
|
|
106
|
+
- Updated dependencies [[`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`4176757`](https://github.com/valbuild/val/commit/41767579b01849473a94779122fd39c07dd99234), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1), [`562d032`](https://github.com/valbuild/val/commit/562d03280c3fcf97a80562e8b017dd2125ac4ad1)]:
|
|
107
|
+
- @valbuild/shared@0.148.0
|
|
108
|
+
- @valbuild/ui@0.148.0
|
|
109
|
+
- @valbuild/core@0.148.0
|
|
110
|
+
|
|
111
|
+
## 0.147.0
|
|
112
|
+
|
|
113
|
+
### Minor Changes
|
|
114
|
+
|
|
115
|
+
- [#876](https://github.com/valbuild/val/pull/876) [`cb04e58`](https://github.com/valbuild/val/commit/cb04e586db1e3ffd20ec1fee744561b1c8f8cffd) Thanks [@freekh](https://github.com/freekh)! - Demo mode: run the Val Studio with no account and no backend (TanStack Start).
|
|
116
|
+
|
|
117
|
+
`<ValApp demo>` starts Val's own server in the visitor's browser tab, and the Studio talks to it there instead of to `/api/val`. Edits show up in the page as they are typed and are held in the tab's memory: nothing is sent anywhere, and a reload starts over. Images and videos can be uploaded too: a small service worker, served by `initValDemoServer()`, hands the upload and the unpublished files to the server in the tab, so an uploaded photo or video shows in the page with nothing leaving the browser. Uploads are capped by their size after encoding. Pair `<ValApp demo>` with `<ValProvider demo>` on the site and `initValDemoServer()` from the new `@valbuild/tanstack/demo` entry on the API route. In a demo the Studio opens with a guided tour, Publish explains what it would do instead of publishing, and Preview in a new tab is shown but disabled.
|
|
118
|
+
|
|
119
|
+
Also:
|
|
120
|
+
|
|
121
|
+
- `@valbuild/server/browser` is a new entry: Val's request handling, bundleable for a browser. The TypeScript compiler, which only publishing and history need, is loaded when they ask for it, so a browser bundle of the server is about 140 KB rather than 3.6 MB.
|
|
122
|
+
- `s.image({ encode })` now scales an image down in browsers that cannot write WebP (Safari), as a JPEG or PNG in the image's own format, instead of uploading it at full size.
|
|
123
|
+
- `GET /profiles` answers with no profiles, rather than an error, when edits are stored locally and no project is configured.
|
|
124
|
+
|
|
125
|
+
### Patch Changes
|
|
126
|
+
|
|
127
|
+
- Updated dependencies [[`2f09cd5`](https://github.com/valbuild/val/commit/2f09cd5e2f4fc37f06917899f24f9ad2ee8d3a8b), [`cb04e58`](https://github.com/valbuild/val/commit/cb04e586db1e3ffd20ec1fee744561b1c8f8cffd), [`cb04e58`](https://github.com/valbuild/val/commit/cb04e586db1e3ffd20ec1fee744561b1c8f8cffd)]:
|
|
128
|
+
- @valbuild/core@0.147.0
|
|
129
|
+
- @valbuild/shared@0.147.0
|
|
130
|
+
- @valbuild/ui@0.147.0
|
|
131
|
+
|
|
3
132
|
## 0.146.4
|
|
4
133
|
|
|
5
134
|
### Patch Changes
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export * from "../../dist/declarations/src/browser/index.js";
|
|
2
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsYnVpbGQtc2VydmVyLWJyb3dzZXIuY2pzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9kaXN0L2RlY2xhcmF0aW9ucy9zcmMvYnJvd3Nlci9pbmRleC5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIn0=
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var core = require('@valbuild/core');
|
|
6
|
+
var ValRouterCore = require('../../dist/ValRouterCore-9bdaaf89.cjs.dev.js');
|
|
7
|
+
require('@valbuild/shared/internal');
|
|
8
|
+
require('zod');
|
|
9
|
+
require('zod-validation-error');
|
|
10
|
+
require('@valbuild/core/fp');
|
|
11
|
+
require('@valbuild/core/patch');
|
|
12
|
+
require('image-size');
|
|
13
|
+
|
|
14
|
+
const NO_SESSIONS = "Val: sessions are not available here -- this server runs in the browser, " + "has no secret to sign them with, and serves without authenticating.";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The platform for a server running in a browser tab.
|
|
18
|
+
*
|
|
19
|
+
* Memory mode only: there is no filesystem for `fs` mode and no credential for
|
|
20
|
+
* `http` mode. Everything else answers as a server without those things does
|
|
21
|
+
* -- no `val login` token, no credential for remote files, no link checks --
|
|
22
|
+
* and the session functions refuse, because a tab has no secret to sign with
|
|
23
|
+
* and the server is constructed with `unsafelyAllowUnauthenticated`, so
|
|
24
|
+
* nothing asks for one.
|
|
25
|
+
*/
|
|
26
|
+
const browserPlatform = {
|
|
27
|
+
createOps: (valModules, options) => {
|
|
28
|
+
if (options.mode !== "memory") {
|
|
29
|
+
throw new Error(`Val: a server in the browser runs in memory mode, not '${options.mode}'.`);
|
|
30
|
+
}
|
|
31
|
+
return new ValRouterCore.ValOpsMemory(valModules, {
|
|
32
|
+
formatter: options.formatter,
|
|
33
|
+
config: options.config,
|
|
34
|
+
sourceFiles: options.sourceFiles,
|
|
35
|
+
patchStore: options.patchStore,
|
|
36
|
+
unsafelyAllowUnauthenticated: options.unsafelyAllowUnauthenticated,
|
|
37
|
+
// Nothing can reach a server in a tab but the page it runs in.
|
|
38
|
+
warnUnauthenticated: false,
|
|
39
|
+
contentUrl: options.valContentUrl
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
probeUrl: async () => ({
|
|
43
|
+
kind: "skipped",
|
|
44
|
+
message: "Links are not checked in the browser."
|
|
45
|
+
}),
|
|
46
|
+
jwt: {
|
|
47
|
+
verifyJwt: () => ({
|
|
48
|
+
success: false,
|
|
49
|
+
reason: "missing-secret",
|
|
50
|
+
message: NO_SESSIONS
|
|
51
|
+
}),
|
|
52
|
+
decodeJwtWithoutVerifying: () => ({
|
|
53
|
+
success: false,
|
|
54
|
+
reason: "missing-secret",
|
|
55
|
+
message: NO_SESSIONS
|
|
56
|
+
}),
|
|
57
|
+
encodeJwt: () => {
|
|
58
|
+
throw new Error(NO_SESSIONS);
|
|
59
|
+
},
|
|
60
|
+
getExpire: () => Math.floor(Date.now() / 1000) + 60 * 60
|
|
61
|
+
},
|
|
62
|
+
bytesToBase64: ValRouterCore.bytesToBase64,
|
|
63
|
+
readValLoginToken: async () => null,
|
|
64
|
+
resolveRemoteFileAuth: async () => ({
|
|
65
|
+
status: "error",
|
|
66
|
+
errorCode: "api-key-missing",
|
|
67
|
+
message: "Remote files are not available in the browser."
|
|
68
|
+
})
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** A `fetch`, as far as the Studio's client calls one. */
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Val's API, served in this tab: a `fetch` that never touches the network.
|
|
75
|
+
*
|
|
76
|
+
* The whole server -- the router's request parsing, `ValServer`'s routes,
|
|
77
|
+
* memory mode's patch chain -- runs here, exactly as it runs on a server.
|
|
78
|
+
* What is removed is the hop between them, and nothing else, which is why the
|
|
79
|
+
* demo built on it cannot drift from the product: there is no second
|
|
80
|
+
* implementation to drift. See `docs/plans/demo-mode.md`.
|
|
81
|
+
*
|
|
82
|
+
* Memory mode with no source files, because nothing here publishes (only a
|
|
83
|
+
* publish reads `.val.ts` text), and without authentication, because the only
|
|
84
|
+
* caller is the page this runs in.
|
|
85
|
+
*/
|
|
86
|
+
function createInTabValFetch(valModules, options) {
|
|
87
|
+
const route = options.route ?? "/api/val";
|
|
88
|
+
const serverConfig = {
|
|
89
|
+
mode: "memory",
|
|
90
|
+
route,
|
|
91
|
+
sourceFiles: {},
|
|
92
|
+
patchStore: options.patchStore,
|
|
93
|
+
unsafelyAllowUnauthenticated: true,
|
|
94
|
+
valContentUrl: core.DEFAULT_CONTENT_HOST,
|
|
95
|
+
config: options.config
|
|
96
|
+
};
|
|
97
|
+
const server = ValRouterCore.ValServer(valModules, serverConfig, {
|
|
98
|
+
// Draft content is the only content a demo has.
|
|
99
|
+
isEnabled: async () => true,
|
|
100
|
+
onEnable: async () => {},
|
|
101
|
+
onDisable: async () => {}
|
|
102
|
+
}, browserPlatform);
|
|
103
|
+
const handle = ValRouterCore.createValRequestHandler(route, Promise.resolve(server), toResponse);
|
|
104
|
+
return (input, init) => {
|
|
105
|
+
const base = typeof window === "undefined" ? "http://localhost" : window.location.origin;
|
|
106
|
+
// A `Request` already has an absolute URL, and `init` overrides what it
|
|
107
|
+
// carries, as it does for `fetch`. Only a string or URL is resolved.
|
|
108
|
+
return handle(input instanceof Request ? new Request(input, init) : new Request(new URL(String(input), base).href, init));
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The server's answer as a `Response`. The same conversion a host makes,
|
|
114
|
+
* less the cookies: a `Response` made in the page cannot set one, and a server
|
|
115
|
+
* that never authenticates has none to set.
|
|
116
|
+
*/
|
|
117
|
+
function toResponse(valRes) {
|
|
118
|
+
const headers = new Headers();
|
|
119
|
+
const valResHeaders = "headers" in valRes && valRes.headers || {};
|
|
120
|
+
for (const key in valResHeaders) {
|
|
121
|
+
const value = valResHeaders[key];
|
|
122
|
+
if (typeof value === "string") {
|
|
123
|
+
headers.set(key, value);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if ("json" in valRes) {
|
|
127
|
+
headers.set("Content-Type", "application/json");
|
|
128
|
+
return new Response(JSON.stringify(valRes.json), {
|
|
129
|
+
headers,
|
|
130
|
+
status: valRes.status
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (valRes.status === 302) {
|
|
134
|
+
headers.set("Location", valRes.redirectTo);
|
|
135
|
+
return new Response(null, {
|
|
136
|
+
status: valRes.status,
|
|
137
|
+
headers
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return new Response("body" in valRes ? valRes.body : null, {
|
|
141
|
+
headers,
|
|
142
|
+
status: valRes.status
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
exports.createInTabValFetch = createInTabValFetch;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var core = require('@valbuild/core');
|
|
6
|
+
var ValRouterCore = require('../../dist/ValRouterCore-42772c21.cjs.prod.js');
|
|
7
|
+
require('@valbuild/shared/internal');
|
|
8
|
+
require('zod');
|
|
9
|
+
require('zod-validation-error');
|
|
10
|
+
require('@valbuild/core/fp');
|
|
11
|
+
require('@valbuild/core/patch');
|
|
12
|
+
require('image-size');
|
|
13
|
+
|
|
14
|
+
const NO_SESSIONS = "Val: sessions are not available here -- this server runs in the browser, " + "has no secret to sign them with, and serves without authenticating.";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The platform for a server running in a browser tab.
|
|
18
|
+
*
|
|
19
|
+
* Memory mode only: there is no filesystem for `fs` mode and no credential for
|
|
20
|
+
* `http` mode. Everything else answers as a server without those things does
|
|
21
|
+
* -- no `val login` token, no credential for remote files, no link checks --
|
|
22
|
+
* and the session functions refuse, because a tab has no secret to sign with
|
|
23
|
+
* and the server is constructed with `unsafelyAllowUnauthenticated`, so
|
|
24
|
+
* nothing asks for one.
|
|
25
|
+
*/
|
|
26
|
+
const browserPlatform = {
|
|
27
|
+
createOps: (valModules, options) => {
|
|
28
|
+
if (options.mode !== "memory") {
|
|
29
|
+
throw new Error(`Val: a server in the browser runs in memory mode, not '${options.mode}'.`);
|
|
30
|
+
}
|
|
31
|
+
return new ValRouterCore.ValOpsMemory(valModules, {
|
|
32
|
+
formatter: options.formatter,
|
|
33
|
+
config: options.config,
|
|
34
|
+
sourceFiles: options.sourceFiles,
|
|
35
|
+
patchStore: options.patchStore,
|
|
36
|
+
unsafelyAllowUnauthenticated: options.unsafelyAllowUnauthenticated,
|
|
37
|
+
// Nothing can reach a server in a tab but the page it runs in.
|
|
38
|
+
warnUnauthenticated: false,
|
|
39
|
+
contentUrl: options.valContentUrl
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
probeUrl: async () => ({
|
|
43
|
+
kind: "skipped",
|
|
44
|
+
message: "Links are not checked in the browser."
|
|
45
|
+
}),
|
|
46
|
+
jwt: {
|
|
47
|
+
verifyJwt: () => ({
|
|
48
|
+
success: false,
|
|
49
|
+
reason: "missing-secret",
|
|
50
|
+
message: NO_SESSIONS
|
|
51
|
+
}),
|
|
52
|
+
decodeJwtWithoutVerifying: () => ({
|
|
53
|
+
success: false,
|
|
54
|
+
reason: "missing-secret",
|
|
55
|
+
message: NO_SESSIONS
|
|
56
|
+
}),
|
|
57
|
+
encodeJwt: () => {
|
|
58
|
+
throw new Error(NO_SESSIONS);
|
|
59
|
+
},
|
|
60
|
+
getExpire: () => Math.floor(Date.now() / 1000) + 60 * 60
|
|
61
|
+
},
|
|
62
|
+
bytesToBase64: ValRouterCore.bytesToBase64,
|
|
63
|
+
readValLoginToken: async () => null,
|
|
64
|
+
resolveRemoteFileAuth: async () => ({
|
|
65
|
+
status: "error",
|
|
66
|
+
errorCode: "api-key-missing",
|
|
67
|
+
message: "Remote files are not available in the browser."
|
|
68
|
+
})
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** A `fetch`, as far as the Studio's client calls one. */
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Val's API, served in this tab: a `fetch` that never touches the network.
|
|
75
|
+
*
|
|
76
|
+
* The whole server -- the router's request parsing, `ValServer`'s routes,
|
|
77
|
+
* memory mode's patch chain -- runs here, exactly as it runs on a server.
|
|
78
|
+
* What is removed is the hop between them, and nothing else, which is why the
|
|
79
|
+
* demo built on it cannot drift from the product: there is no second
|
|
80
|
+
* implementation to drift. See `docs/plans/demo-mode.md`.
|
|
81
|
+
*
|
|
82
|
+
* Memory mode with no source files, because nothing here publishes (only a
|
|
83
|
+
* publish reads `.val.ts` text), and without authentication, because the only
|
|
84
|
+
* caller is the page this runs in.
|
|
85
|
+
*/
|
|
86
|
+
function createInTabValFetch(valModules, options) {
|
|
87
|
+
const route = options.route ?? "/api/val";
|
|
88
|
+
const serverConfig = {
|
|
89
|
+
mode: "memory",
|
|
90
|
+
route,
|
|
91
|
+
sourceFiles: {},
|
|
92
|
+
patchStore: options.patchStore,
|
|
93
|
+
unsafelyAllowUnauthenticated: true,
|
|
94
|
+
valContentUrl: core.DEFAULT_CONTENT_HOST,
|
|
95
|
+
config: options.config
|
|
96
|
+
};
|
|
97
|
+
const server = ValRouterCore.ValServer(valModules, serverConfig, {
|
|
98
|
+
// Draft content is the only content a demo has.
|
|
99
|
+
isEnabled: async () => true,
|
|
100
|
+
onEnable: async () => {},
|
|
101
|
+
onDisable: async () => {}
|
|
102
|
+
}, browserPlatform);
|
|
103
|
+
const handle = ValRouterCore.createValRequestHandler(route, Promise.resolve(server), toResponse);
|
|
104
|
+
return (input, init) => {
|
|
105
|
+
const base = typeof window === "undefined" ? "http://localhost" : window.location.origin;
|
|
106
|
+
// A `Request` already has an absolute URL, and `init` overrides what it
|
|
107
|
+
// carries, as it does for `fetch`. Only a string or URL is resolved.
|
|
108
|
+
return handle(input instanceof Request ? new Request(input, init) : new Request(new URL(String(input), base).href, init));
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The server's answer as a `Response`. The same conversion a host makes,
|
|
114
|
+
* less the cookies: a `Response` made in the page cannot set one, and a server
|
|
115
|
+
* that never authenticates has none to set.
|
|
116
|
+
*/
|
|
117
|
+
function toResponse(valRes) {
|
|
118
|
+
const headers = new Headers();
|
|
119
|
+
const valResHeaders = "headers" in valRes && valRes.headers || {};
|
|
120
|
+
for (const key in valResHeaders) {
|
|
121
|
+
const value = valResHeaders[key];
|
|
122
|
+
if (typeof value === "string") {
|
|
123
|
+
headers.set(key, value);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if ("json" in valRes) {
|
|
127
|
+
headers.set("Content-Type", "application/json");
|
|
128
|
+
return new Response(JSON.stringify(valRes.json), {
|
|
129
|
+
headers,
|
|
130
|
+
status: valRes.status
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (valRes.status === 302) {
|
|
134
|
+
headers.set("Location", valRes.redirectTo);
|
|
135
|
+
return new Response(null, {
|
|
136
|
+
status: valRes.status,
|
|
137
|
+
headers
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return new Response("body" in valRes ? valRes.body : null, {
|
|
141
|
+
headers,
|
|
142
|
+
status: valRes.status
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
exports.createInTabValFetch = createInTabValFetch;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { DEFAULT_CONTENT_HOST } from '@valbuild/core';
|
|
2
|
+
import { j as ValOpsMemory, E as bytesToBase64, y as ValServer, z as createValRequestHandler } from '../../dist/ValRouterCore-7ca3dc68.esm.js';
|
|
3
|
+
import '@valbuild/shared/internal';
|
|
4
|
+
import 'zod';
|
|
5
|
+
import 'zod-validation-error';
|
|
6
|
+
import '@valbuild/core/fp';
|
|
7
|
+
import '@valbuild/core/patch';
|
|
8
|
+
import 'image-size';
|
|
9
|
+
|
|
10
|
+
const NO_SESSIONS = "Val: sessions are not available here -- this server runs in the browser, " + "has no secret to sign them with, and serves without authenticating.";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The platform for a server running in a browser tab.
|
|
14
|
+
*
|
|
15
|
+
* Memory mode only: there is no filesystem for `fs` mode and no credential for
|
|
16
|
+
* `http` mode. Everything else answers as a server without those things does
|
|
17
|
+
* -- no `val login` token, no credential for remote files, no link checks --
|
|
18
|
+
* and the session functions refuse, because a tab has no secret to sign with
|
|
19
|
+
* and the server is constructed with `unsafelyAllowUnauthenticated`, so
|
|
20
|
+
* nothing asks for one.
|
|
21
|
+
*/
|
|
22
|
+
const browserPlatform = {
|
|
23
|
+
createOps: (valModules, options) => {
|
|
24
|
+
if (options.mode !== "memory") {
|
|
25
|
+
throw new Error(`Val: a server in the browser runs in memory mode, not '${options.mode}'.`);
|
|
26
|
+
}
|
|
27
|
+
return new ValOpsMemory(valModules, {
|
|
28
|
+
formatter: options.formatter,
|
|
29
|
+
config: options.config,
|
|
30
|
+
sourceFiles: options.sourceFiles,
|
|
31
|
+
patchStore: options.patchStore,
|
|
32
|
+
unsafelyAllowUnauthenticated: options.unsafelyAllowUnauthenticated,
|
|
33
|
+
// Nothing can reach a server in a tab but the page it runs in.
|
|
34
|
+
warnUnauthenticated: false,
|
|
35
|
+
contentUrl: options.valContentUrl
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
probeUrl: async () => ({
|
|
39
|
+
kind: "skipped",
|
|
40
|
+
message: "Links are not checked in the browser."
|
|
41
|
+
}),
|
|
42
|
+
jwt: {
|
|
43
|
+
verifyJwt: () => ({
|
|
44
|
+
success: false,
|
|
45
|
+
reason: "missing-secret",
|
|
46
|
+
message: NO_SESSIONS
|
|
47
|
+
}),
|
|
48
|
+
decodeJwtWithoutVerifying: () => ({
|
|
49
|
+
success: false,
|
|
50
|
+
reason: "missing-secret",
|
|
51
|
+
message: NO_SESSIONS
|
|
52
|
+
}),
|
|
53
|
+
encodeJwt: () => {
|
|
54
|
+
throw new Error(NO_SESSIONS);
|
|
55
|
+
},
|
|
56
|
+
getExpire: () => Math.floor(Date.now() / 1000) + 60 * 60
|
|
57
|
+
},
|
|
58
|
+
bytesToBase64,
|
|
59
|
+
readValLoginToken: async () => null,
|
|
60
|
+
resolveRemoteFileAuth: async () => ({
|
|
61
|
+
status: "error",
|
|
62
|
+
errorCode: "api-key-missing",
|
|
63
|
+
message: "Remote files are not available in the browser."
|
|
64
|
+
})
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/** A `fetch`, as far as the Studio's client calls one. */
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Val's API, served in this tab: a `fetch` that never touches the network.
|
|
71
|
+
*
|
|
72
|
+
* The whole server -- the router's request parsing, `ValServer`'s routes,
|
|
73
|
+
* memory mode's patch chain -- runs here, exactly as it runs on a server.
|
|
74
|
+
* What is removed is the hop between them, and nothing else, which is why the
|
|
75
|
+
* demo built on it cannot drift from the product: there is no second
|
|
76
|
+
* implementation to drift. See `docs/plans/demo-mode.md`.
|
|
77
|
+
*
|
|
78
|
+
* Memory mode with no source files, because nothing here publishes (only a
|
|
79
|
+
* publish reads `.val.ts` text), and without authentication, because the only
|
|
80
|
+
* caller is the page this runs in.
|
|
81
|
+
*/
|
|
82
|
+
function createInTabValFetch(valModules, options) {
|
|
83
|
+
const route = options.route ?? "/api/val";
|
|
84
|
+
const serverConfig = {
|
|
85
|
+
mode: "memory",
|
|
86
|
+
route,
|
|
87
|
+
sourceFiles: {},
|
|
88
|
+
patchStore: options.patchStore,
|
|
89
|
+
unsafelyAllowUnauthenticated: true,
|
|
90
|
+
valContentUrl: DEFAULT_CONTENT_HOST,
|
|
91
|
+
config: options.config
|
|
92
|
+
};
|
|
93
|
+
const server = ValServer(valModules, serverConfig, {
|
|
94
|
+
// Draft content is the only content a demo has.
|
|
95
|
+
isEnabled: async () => true,
|
|
96
|
+
onEnable: async () => {},
|
|
97
|
+
onDisable: async () => {}
|
|
98
|
+
}, browserPlatform);
|
|
99
|
+
const handle = createValRequestHandler(route, Promise.resolve(server), toResponse);
|
|
100
|
+
return (input, init) => {
|
|
101
|
+
const base = typeof window === "undefined" ? "http://localhost" : window.location.origin;
|
|
102
|
+
// A `Request` already has an absolute URL, and `init` overrides what it
|
|
103
|
+
// carries, as it does for `fetch`. Only a string or URL is resolved.
|
|
104
|
+
return handle(input instanceof Request ? new Request(input, init) : new Request(new URL(String(input), base).href, init));
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The server's answer as a `Response`. The same conversion a host makes,
|
|
110
|
+
* less the cookies: a `Response` made in the page cannot set one, and a server
|
|
111
|
+
* that never authenticates has none to set.
|
|
112
|
+
*/
|
|
113
|
+
function toResponse(valRes) {
|
|
114
|
+
const headers = new Headers();
|
|
115
|
+
const valResHeaders = "headers" in valRes && valRes.headers || {};
|
|
116
|
+
for (const key in valResHeaders) {
|
|
117
|
+
const value = valResHeaders[key];
|
|
118
|
+
if (typeof value === "string") {
|
|
119
|
+
headers.set(key, value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if ("json" in valRes) {
|
|
123
|
+
headers.set("Content-Type", "application/json");
|
|
124
|
+
return new Response(JSON.stringify(valRes.json), {
|
|
125
|
+
headers,
|
|
126
|
+
status: valRes.status
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (valRes.status === 302) {
|
|
130
|
+
headers.set("Location", valRes.redirectTo);
|
|
131
|
+
return new Response(null, {
|
|
132
|
+
status: valRes.status,
|
|
133
|
+
headers
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return new Response("body" in valRes ? valRes.body : null, {
|
|
137
|
+
headers,
|
|
138
|
+
status: valRes.status
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { createInTabValFetch };
|