@tinvar/renderer 0.0.0-stage → 0.4.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/LICENSE +21 -0
- package/bin/dev-renderer.js +117 -0
- package/bin/renderer-probe.js +52 -0
- package/bin/renderer.js +327 -0
- package/package.json +44 -4
- package/src/asset-origin.js +306 -0
- package/src/build-profile.js +108 -0
- package/src/can-branch.js +1666 -0
- package/src/classify.js +157 -0
- package/src/client-chunks.js +54 -0
- package/src/client-props.js +540 -0
- package/src/codec.js +297 -0
- package/src/dev-app.js +45 -0
- package/src/dev-guard.js +33 -0
- package/src/dev-server.js +460 -0
- package/src/eslint.js +469 -0
- package/src/head.js +524 -0
- package/src/html-stream.js +479 -0
- package/src/log.js +131 -0
- package/src/mint-client.js +142 -0
- package/src/panel-styles.js +254 -0
- package/src/pool.js +664 -0
- package/src/protocol.js +71 -0
- package/src/query-client.js +198 -0
- package/src/query-map.js +116 -0
- package/src/render-session.js +580 -0
- package/src/sandbox.js +710 -0
- package/src/server.js +466 -0
- package/src/socket-group.js +72 -0
- package/src/vite.js +2416 -0
- package/src/vitest.d.ts +20 -0
- package/src/vitest.js +179 -0
- package/src/warm.d.ts +9 -0
- package/src/warm.js +60 -0
- package/src/workbench.d.ts +146 -0
- package/src/workbench.js +754 -0
- package/README.md +0 -3
package/src/vitest.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
/** Where `tinvar codegen` writes the mocks and fakes, under the types directory. */
|
|
4
|
+
export declare const TESTING_DIRECTORY: 'testing';
|
|
5
|
+
|
|
6
|
+
/** Throws unless this is a Vitest run, outside production, and not a build. */
|
|
7
|
+
export declare function assertTestRun(
|
|
8
|
+
env: Record<string, string | undefined>,
|
|
9
|
+
command: string | undefined,
|
|
10
|
+
): void;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* For a Vitest config: `@queries/*` and `@actions/*` resolved to codegen's
|
|
14
|
+
* mocks, and each 'use client' module's exports marked for renderServer().
|
|
15
|
+
* Refuses a build, and any process that says production. See ADR 0061.
|
|
16
|
+
*/
|
|
17
|
+
export declare function tinvarTesting(options?: { types?: string }): Plugin[];
|
|
18
|
+
|
|
19
|
+
/** The local names a 'use client' module exports, or null when it is not one. */
|
|
20
|
+
export declare function clientExports(code: string): string[] | null;
|
package/src/vitest.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// The Vitest side of the testing layer: component tests against codegen's
|
|
2
|
+
// mocks (tinvar-spec.md, "Testing", "Client components"; ADR 0061).
|
|
3
|
+
//
|
|
4
|
+
// // vitest.config.ts
|
|
5
|
+
// plugins: [tinvarTesting()]
|
|
6
|
+
//
|
|
7
|
+
// Two things a component test needs and a build must never have:
|
|
8
|
+
//
|
|
9
|
+
// - `@queries/*` and `@actions/*` resolved to the mocks `tinvar codegen`
|
|
10
|
+
// writes under resources/types/testing, which record their calls and
|
|
11
|
+
// answer with fakes of the PHP Data classes. In the build the same
|
|
12
|
+
// specifiers are the real functions (queryFunctions()) and references
|
|
13
|
+
// (the app's alias); here nothing posts and nothing reaches PHP.
|
|
14
|
+
// - Each 'use client' module's exports marked, so renderServer() in
|
|
15
|
+
// @tinvar/react/testing knows which components to call as server
|
|
16
|
+
// components and which to leave to React, as the RSC build's split on
|
|
17
|
+
// the same directive does.
|
|
18
|
+
//
|
|
19
|
+
// It refuses a build, a process that says production, and anything that is
|
|
20
|
+
// not a Vitest run, before it does either.
|
|
21
|
+
|
|
22
|
+
import { existsSync } from 'node:fs';
|
|
23
|
+
import { resolve } from 'node:path';
|
|
24
|
+
import { parseAst } from 'vite';
|
|
25
|
+
import { PRODUCTION_MARKERS } from './dev-guard.js';
|
|
26
|
+
|
|
27
|
+
/** Where `tinvar codegen` writes the mocks and fakes, under the types directory. */
|
|
28
|
+
export const TESTING_DIRECTORY = 'testing';
|
|
29
|
+
|
|
30
|
+
/** `@queries` and `@queries/a/b`: a codegen module, by its path segments. */
|
|
31
|
+
const QUERIES_MODULE = /^@queries((?:\/[A-Za-z0-9_-]+)*)$/;
|
|
32
|
+
|
|
33
|
+
/** `@actions/RackActions`: one module per class with actions. */
|
|
34
|
+
const ACTIONS_MODULE = /^@actions\/([A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
35
|
+
|
|
36
|
+
/** Anything under either prefix, so a specifier that is neither of those shapes is refused, not passed on. */
|
|
37
|
+
const MOCKED_PREFIX = /^@(?:queries|actions)(?:\/|$)/;
|
|
38
|
+
|
|
39
|
+
const SOURCE = /\.(?:[cm]?[jt]sx?)(?:\?.*)?$/;
|
|
40
|
+
|
|
41
|
+
const TESTING_SPECIFIER = '@tinvar/react/testing';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Throws unless this is a Vitest run, outside production.
|
|
45
|
+
*
|
|
46
|
+
* @param {Record<string, string | undefined>} env
|
|
47
|
+
* @param {string | undefined} command
|
|
48
|
+
*/
|
|
49
|
+
export function assertTestRun(env, command) {
|
|
50
|
+
if (command === 'build') {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'[tinvar] tinvarTesting() is in a build. It belongs in a Vitest config, never a Vite build.',
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
for (const name of PRODUCTION_MARKERS) {
|
|
56
|
+
if (
|
|
57
|
+
String(env[name] ?? '')
|
|
58
|
+
.trim()
|
|
59
|
+
.toLowerCase() === 'production'
|
|
60
|
+
) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`[tinvar] tinvarTesting() is refused: ${name}=production. It puts mocks where queries and actions are.`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!env.VITEST) {
|
|
67
|
+
throw new Error('[tinvar] tinvarTesting() runs only under Vitest, and VITEST is not set.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {{ types?: string }} [options] `types` is where `tinvar codegen`
|
|
73
|
+
* writes, relative to the Vite root
|
|
74
|
+
* @returns {import('vite').Plugin[]}
|
|
75
|
+
*/
|
|
76
|
+
export function tinvarTesting({ types = 'resources/types' } = {}) {
|
|
77
|
+
let root = process.cwd();
|
|
78
|
+
|
|
79
|
+
/** @param {string} relative */
|
|
80
|
+
const mock = (relative) => resolve(root, types, TESTING_DIRECTORY, relative);
|
|
81
|
+
|
|
82
|
+
return [
|
|
83
|
+
{
|
|
84
|
+
name: 'tinvar:testing-mocks',
|
|
85
|
+
enforce: 'pre',
|
|
86
|
+
config(_config, env) {
|
|
87
|
+
assertTestRun(process.env, env.command);
|
|
88
|
+
},
|
|
89
|
+
configResolved(config) {
|
|
90
|
+
root = config.root;
|
|
91
|
+
},
|
|
92
|
+
resolveId(source, importer) {
|
|
93
|
+
if (!MOCKED_PREFIX.test(source)) return null;
|
|
94
|
+
const query = QUERIES_MODULE.exec(source);
|
|
95
|
+
const action = ACTIONS_MODULE.exec(source);
|
|
96
|
+
const file =
|
|
97
|
+
query !== null
|
|
98
|
+
? mock(query[1] === '' ? 'queries.ts' : `queries${query[1]}.ts`)
|
|
99
|
+
: action !== null
|
|
100
|
+
? mock(`actions/${action[1]}.ts`)
|
|
101
|
+
: null;
|
|
102
|
+
const from = importer ? ` (imported by ${importer})` : '';
|
|
103
|
+
if (file === null) {
|
|
104
|
+
this.error(`[tinvar] ${source}${from} is not a module tinvar codegen writes.`);
|
|
105
|
+
}
|
|
106
|
+
if (!existsSync(file)) {
|
|
107
|
+
this.error(`[tinvar] ${source}${from} has no mock: tinvar codegen writes one to ${file}; run it.`);
|
|
108
|
+
}
|
|
109
|
+
return file;
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
// After TypeScript and JSX are compiled away, so the module parses as
|
|
114
|
+
// JavaScript; and on every module Vitest transforms, an inlined
|
|
115
|
+
// dependency's included.
|
|
116
|
+
name: 'tinvar:testing-client-components',
|
|
117
|
+
transform(code, id) {
|
|
118
|
+
if (!SOURCE.test(id) || !code.includes('use client')) return null;
|
|
119
|
+
const names = clientExports(code);
|
|
120
|
+
if (names === null || names.length === 0) return null;
|
|
121
|
+
return {
|
|
122
|
+
code:
|
|
123
|
+
`${code}\n;import { markClient as __tinvarMarkClient } from ${JSON.stringify(TESTING_SPECIFIER)};\n` +
|
|
124
|
+
`__tinvarMarkClient(${names.join(', ')});\n`,
|
|
125
|
+
map: null,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The local names a 'use client' module exports, or null when it is not
|
|
134
|
+
* one. A directive is only in the prologue: the string anywhere else is not
|
|
135
|
+
* one. An anonymous default export has no name to mark, and is left out --
|
|
136
|
+
* renderServer() would call it as a server component, and says so if it
|
|
137
|
+
* then calls a hook.
|
|
138
|
+
*
|
|
139
|
+
* @param {string} code
|
|
140
|
+
* @returns {string[] | null}
|
|
141
|
+
*/
|
|
142
|
+
export function clientExports(code) {
|
|
143
|
+
const program = /** @type {any} */ (parseAst(code));
|
|
144
|
+
let client = false;
|
|
145
|
+
for (const statement of program.body) {
|
|
146
|
+
if (statement.type !== 'ExpressionStatement' || typeof statement.directive !== 'string') break;
|
|
147
|
+
if (statement.directive === 'use client') client = true;
|
|
148
|
+
}
|
|
149
|
+
if (!client) return null;
|
|
150
|
+
|
|
151
|
+
/** @type {Set<string>} */
|
|
152
|
+
const names = new Set();
|
|
153
|
+
for (const statement of program.body) {
|
|
154
|
+
if (statement.type === 'ExportNamedDeclaration' && statement.source == null) {
|
|
155
|
+
const declaration = statement.declaration;
|
|
156
|
+
if (declaration?.type === 'FunctionDeclaration' || declaration?.type === 'ClassDeclaration') {
|
|
157
|
+
if (declaration.id) names.add(declaration.id.name);
|
|
158
|
+
} else if (declaration?.type === 'VariableDeclaration') {
|
|
159
|
+
for (const declarator of declaration.declarations) {
|
|
160
|
+
if (declarator.id.type === 'Identifier') names.add(declarator.id.name);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
164
|
+
if (specifier.local?.type === 'Identifier') names.add(specifier.local.name);
|
|
165
|
+
}
|
|
166
|
+
} else if (statement.type === 'ExportDefaultDeclaration') {
|
|
167
|
+
const declaration = statement.declaration;
|
|
168
|
+
if (
|
|
169
|
+
(declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') &&
|
|
170
|
+
declaration.id
|
|
171
|
+
) {
|
|
172
|
+
names.add(declaration.id.name);
|
|
173
|
+
} else if (declaration.type === 'Identifier') {
|
|
174
|
+
names.add(declaration.name);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return [...names];
|
|
179
|
+
}
|
package/src/warm.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads every client component's module through the RSC plugin's ssr
|
|
3
|
+
* loader before the first render, marking each load settled, so the first
|
|
4
|
+
* page's head preloads every module a later one's would.
|
|
5
|
+
*/
|
|
6
|
+
export declare function warmClientReferences(
|
|
7
|
+
references: Record<string, unknown> | null | undefined,
|
|
8
|
+
loader?: unknown,
|
|
9
|
+
): Promise<{ loaded: number; failed: string[] }>;
|
package/src/warm.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Every client component's module loaded before the first render, so the
|
|
2
|
+
// first page preloads what the hundredth does.
|
|
3
|
+
//
|
|
4
|
+
// The ssr environment loads a client component's module the first time a
|
|
5
|
+
// payload names it. The RSC plugin hints its chunks for preloading as the
|
|
6
|
+
// module is reached, and React keeps a hint only while it is rendering: the
|
|
7
|
+
// first time, the module's load settles while the payload is still being
|
|
8
|
+
// decoded, the component is reached then, outside React's render, and the
|
|
9
|
+
// hint is dropped. That page's head named fewer modules than every page
|
|
10
|
+
// after it -- in the browser, a chunk fetched as the payload named it, and a
|
|
11
|
+
// shorter Link header. Once a module has loaded, and the decoder has seen
|
|
12
|
+
// it settle, the component is reached as React renders it, and the hint is
|
|
13
|
+
// kept.
|
|
14
|
+
//
|
|
15
|
+
// So the renderer loads every one before it says it is ready, through the
|
|
16
|
+
// same loader the decoder asks -- the plugin's, which memoizes each load --
|
|
17
|
+
// and marks each load settled the way React's decoder does, on the promise
|
|
18
|
+
// itself, so the first payload finds them all already there.
|
|
19
|
+
//
|
|
20
|
+
// The ssr entry calls it with the plugin's own map of the client references
|
|
21
|
+
// (`virtual:vite-rsc/client-references`, a module only an app's own build
|
|
22
|
+
// can import), and the rsc entry hands it to the renderer as `warm()`,
|
|
23
|
+
// which the renderer awaits before it listens. A module that does not load
|
|
24
|
+
// is not the start's to fail: it is the page's, which fails as it did.
|
|
25
|
+
|
|
26
|
+
/** The loader the RSC plugin's ssr runtime installs for client references. */
|
|
27
|
+
const LOADER = '__vite_rsc_client_require__';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {Record<string, unknown> | null | undefined} references the plugin's
|
|
31
|
+
* map of client references, by id
|
|
32
|
+
* @param {unknown} [loader] the plugin's loader; the global one by default
|
|
33
|
+
* @returns {Promise<{ loaded: number, failed: string[] }>}
|
|
34
|
+
*/
|
|
35
|
+
export async function warmClientReferences(references, loader = /** @type {any} */ (globalThis)[LOADER]) {
|
|
36
|
+
if (typeof loader !== 'function' || references === null || typeof references !== 'object') {
|
|
37
|
+
return { loaded: 0, failed: [] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ids = Object.keys(references);
|
|
41
|
+
const failed = [];
|
|
42
|
+
await Promise.all(
|
|
43
|
+
ids.map(async (id) => {
|
|
44
|
+
try {
|
|
45
|
+
const loading = loader(id);
|
|
46
|
+
const value = await loading;
|
|
47
|
+
// As React's decoder marks a module load it has seen settle: it then
|
|
48
|
+
// reads the module where it is rendering, instead of waiting for it.
|
|
49
|
+
if (loading !== null && typeof loading === 'object' && typeof loading.then === 'function') {
|
|
50
|
+
loading.status = 'fulfilled';
|
|
51
|
+
loading.value = value;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
failed.push(id);
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return { loaded: ids.length - failed.length, failed: failed.sort() };
|
|
60
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { Plugin } from 'vite';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* How a sample answers a query, or a shared read (`$shared.<key>`): a value,
|
|
7
|
+
* or a function of the call's arguments.
|
|
8
|
+
*/
|
|
9
|
+
export type QueryAnswers = Record<string, unknown | ((args: Record<string, unknown>) => unknown)>;
|
|
10
|
+
|
|
11
|
+
/** One way of drawing a page: its loaders' data, the route's parameters, and the queries its components call. */
|
|
12
|
+
export interface PageSample {
|
|
13
|
+
/** What the page's loader returns, as the encoder would send it. */
|
|
14
|
+
page: unknown;
|
|
15
|
+
/** Each layout's loader data, by the layout's key. */
|
|
16
|
+
layouts?: Record<string, unknown>;
|
|
17
|
+
params?: Record<string, string>;
|
|
18
|
+
queries?: QueryAnswers;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** One way of drawing a component: its props, and the queries it calls. */
|
|
22
|
+
export interface ComponentSample {
|
|
23
|
+
props: Record<string, unknown>;
|
|
24
|
+
queries?: QueryAnswers;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface WorkbenchPage {
|
|
28
|
+
/** The view the page renders, as the rsc entry's route table names it. */
|
|
29
|
+
route: string;
|
|
30
|
+
samples: Record<string, PageSample>;
|
|
31
|
+
/** Answers every sample shares; a sample's own win. */
|
|
32
|
+
queries?: QueryAnswers;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface WorkbenchComponent<P = Record<string, unknown>> {
|
|
36
|
+
/** The component drawn with a sample's props. */
|
|
37
|
+
render: (props: P) => ReactNode;
|
|
38
|
+
samples: Record<string, ComponentSample>;
|
|
39
|
+
queries?: QueryAnswers;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** What a frame of the workbench carries: a render's id, its nonce, and the queries it may call. */
|
|
43
|
+
export interface WorkbenchFrame {
|
|
44
|
+
rid: string;
|
|
45
|
+
nonce: string;
|
|
46
|
+
allow: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface WorkbenchPageFrame extends WorkbenchFrame {
|
|
50
|
+
route: string;
|
|
51
|
+
params: Record<string, string>;
|
|
52
|
+
data: { page: unknown; layouts: Record<string, unknown> };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface WorkbenchQueryClient {
|
|
56
|
+
call(name: string, args: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface WorkbenchMintClient {
|
|
60
|
+
mint(component: string, props: Record<string, unknown>): Promise<string>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The app's workbench module's default export, loaded in the rsc environment
|
|
65
|
+
* by the dev server alone. Its renders answer RSC streams.
|
|
66
|
+
*/
|
|
67
|
+
export interface Workbench {
|
|
68
|
+
pages?: Record<string, WorkbenchPage>;
|
|
69
|
+
components?: Record<string, WorkbenchComponent<never>>;
|
|
70
|
+
renderPage(
|
|
71
|
+
frame: WorkbenchPageFrame,
|
|
72
|
+
client: WorkbenchQueryClient,
|
|
73
|
+
mints: WorkbenchMintClient,
|
|
74
|
+
): Promise<ReadableStream<Uint8Array>>;
|
|
75
|
+
renderElement(
|
|
76
|
+
element: ReactNode,
|
|
77
|
+
frame: WorkbenchFrame,
|
|
78
|
+
client: WorkbenchQueryClient,
|
|
79
|
+
mints: WorkbenchMintClient,
|
|
80
|
+
): Promise<ReadableStream<Uint8Array>>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The app's module in the ssr environment that turns a render's RSC stream into its whole HTML. */
|
|
84
|
+
export interface WorkbenchSsr {
|
|
85
|
+
prerenderHtml(rscStream: ReadableStream<Uint8Array>): Promise<ReadableStream<Uint8Array>>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface WorkbenchOptions {
|
|
89
|
+
/** The app's workbench module. */
|
|
90
|
+
module: string;
|
|
91
|
+
/** The app's module whose `prerenderHtml()` draws the HTML (WorkbenchSsr). */
|
|
92
|
+
ssr: string;
|
|
93
|
+
/** The Vite environment the workbench module is loaded in; `rsc` unless given. */
|
|
94
|
+
environment?: string;
|
|
95
|
+
/** And the one the ssr module is; `ssr` unless given. */
|
|
96
|
+
ssrEnvironment?: string;
|
|
97
|
+
/** Where the pages are served under the dev server's base; `__workbench` unless given. */
|
|
98
|
+
workbench?: string;
|
|
99
|
+
/** Where the components are; `__playground` unless given. */
|
|
100
|
+
playground?: string;
|
|
101
|
+
/** Styles the app's tooling injects with a script in dev, for the frame, which runs none. */
|
|
102
|
+
css?: () => string | Promise<string>;
|
|
103
|
+
/**
|
|
104
|
+
* The app's client module for a frame drawn with its client script: it reads the payload from
|
|
105
|
+
* the data block `PAYLOAD_ID` names and hydrates the document. Without one, frames run no script.
|
|
106
|
+
*/
|
|
107
|
+
client?: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export declare const NON_PRODUCTION: readonly string[];
|
|
111
|
+
export declare const MAX_PROPS_BYTES: number;
|
|
112
|
+
export declare class WorkbenchRefused extends Error {}
|
|
113
|
+
|
|
114
|
+
/** Throws where anything says production; why it is not mounted, or null where it is. */
|
|
115
|
+
export declare function workbenchRefusal(env?: Record<string, string | undefined>): string | null;
|
|
116
|
+
|
|
117
|
+
/** From this machine, naming this machine, through any proxy of this machine's. */
|
|
118
|
+
export declare function fromThisMachine(req: IncomingMessage): boolean;
|
|
119
|
+
|
|
120
|
+
/** HTML for a frame that runs no script, with `css` in its head. */
|
|
121
|
+
export declare function forTheFrame(html: string, css?: string): string;
|
|
122
|
+
|
|
123
|
+
/** The id of the data block holding a scripted frame's payload rows. */
|
|
124
|
+
export declare const PAYLOAD_ID: string;
|
|
125
|
+
|
|
126
|
+
/** Whether a request is one a scripted frame made of the dev server's modules: allowed `Origin: null`. */
|
|
127
|
+
export declare function forScriptedFrame(req: IncomingMessage, base: string): boolean;
|
|
128
|
+
|
|
129
|
+
/** HTML for a frame that runs the app's client module under `nonce`, its payload in a data block. */
|
|
130
|
+
export declare function scriptedFrame(
|
|
131
|
+
html: string,
|
|
132
|
+
css: string,
|
|
133
|
+
rows: unknown[],
|
|
134
|
+
nonce: string,
|
|
135
|
+
client: string,
|
|
136
|
+
refresh: string | null,
|
|
137
|
+
): string;
|
|
138
|
+
|
|
139
|
+
/** An RSC payload as inline payload rows: text, or `{ $b64 }` where it is not UTF-8. */
|
|
140
|
+
export declare function payloadRows(bytes: Uint8Array): unknown[];
|
|
141
|
+
|
|
142
|
+
/** A query client answering from a sample alone. */
|
|
143
|
+
export declare function sampleQueries(answers: QueryAnswers): WorkbenchQueryClient;
|
|
144
|
+
|
|
145
|
+
/** The dev server's workbench and playground; no part of a build, and refused outside a developer's machine. */
|
|
146
|
+
export declare function workbench(options: WorkbenchOptions): Plugin;
|