@dudousxd/nestjs-agent-react 0.32.0 → 0.34.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/README.md +7 -0
- package/dist/{client-C7eu9WMJ.d.cts → client-BdHUuqy-.d.cts} +36 -1
- package/dist/{client-C7eu9WMJ.d.ts → client-BdHUuqy-.d.ts} +36 -1
- package/dist/{generative-ui-VTZ1EADD.d.cts → generative-ui-CyHY8Lh1.d.cts} +32 -101
- package/dist/{generative-ui-VTZ1EADD.d.ts → generative-ui-DdUZPCXX.d.ts} +32 -101
- package/dist/genui-json-render.cjs +51 -8
- package/dist/genui-json-render.cjs.map +1 -1
- package/dist/genui-json-render.d.cts +2 -1
- package/dist/genui-json-render.d.ts +2 -1
- package/dist/genui-json-render.js +51 -8
- package/dist/genui-json-render.js.map +1 -1
- package/dist/genui-server-playwright.cjs +199 -0
- package/dist/genui-server-playwright.cjs.map +1 -0
- package/dist/genui-server-playwright.d.cts +18 -0
- package/dist/genui-server-playwright.d.ts +18 -0
- package/dist/genui-server-playwright.js +162 -0
- package/dist/genui-server-playwright.js.map +1 -0
- package/dist/genui-server.cjs +188 -0
- package/dist/genui-server.cjs.map +1 -0
- package/dist/genui-server.d.cts +36 -0
- package/dist/genui-server.d.ts +36 -0
- package/dist/genui-server.js +163 -0
- package/dist/genui-server.js.map +1 -0
- package/dist/genui.cjs +127 -8
- package/dist/genui.cjs.map +1 -1
- package/dist/genui.d.cts +5 -2
- package/dist/genui.d.ts +5 -2
- package/dist/genui.js +128 -8
- package/dist/genui.js.map +1 -1
- package/dist/index.cjs +559 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -10
- package/dist/index.d.ts +35 -10
- package/dist/index.js +552 -120
- package/dist/index.js.map +1 -1
- package/dist/media.cjs.map +1 -1
- package/dist/media.d.cts +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/media.js.map +1 -1
- package/dist/react-registry-Di4IVrw-.d.cts +25 -0
- package/dist/react-registry-z9GFbTm9.d.ts +25 -0
- package/dist/types-DZtFsWec.d.cts +99 -0
- package/dist/types-DZtFsWec.d.ts +99 -0
- package/package.json +35 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/genui/server.ts","../src/genui/playwright.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ComponentPresentation } from '@dudousxd/nestjs-agent-core/genui';\nimport { type ReactNode, createElement } from 'react';\nimport { renderToStaticMarkup } from 'react-dom/server';\nimport type { ReactComponentRegistry } from './react-registry.js';\nexport { createReactComponentRegistry, type ReactComponentRegistry } from './react-registry.js';\n\nexport interface CaptureOptions {\n width?: number;\n height?: number;\n timeoutMs?: number;\n}\nexport interface CaptureAdapter {\n images(html: readonly string[], options: CaptureOptions): Promise<Uint8Array[]>;\n pdf(html: readonly string[], options: CaptureOptions): Promise<Uint8Array>;\n}\nexport interface ServerRenderOptions extends CaptureOptions {\n rowsPerPage?: number;\n capture?: CaptureAdapter;\n}\nexport interface ReactServerRendererOptions {\n registry: ReactComponentRegistry;\n /** Trusted app CSS, or a trusted filesystem path; never supply user CSS. */\n stylesheet?: string | { path: string };\n theme?: string;\n capture?: CaptureAdapter;\n}\nexport function boundedCaptureOptions(options: CaptureOptions): Required<CaptureOptions> {\n const result = {\n width: options.width ?? 1200,\n height: options.height ?? 1600,\n timeoutMs: options.timeoutMs ?? 15000,\n };\n for (const [key, value, min, max] of [\n ['width', result.width, 320, 4096],\n ['height', result.height, 200, 16384],\n ['timeoutMs', result.timeoutMs, 100, 60000],\n ] as const) {\n if (!Number.isInteger(value) || value < min || value > max)\n throw new RangeError(`genui: ${key} must be ${min}–${max}`);\n }\n return result;\n}\nexport function createReactServerRenderer(options: ReactServerRendererOptions) {\n async function content(presentation: ComponentPresentation<object>) {\n const rendered = await options.registry.render(presentation, 'react');\n return renderToStaticMarkup(\n createElement(\n 'main',\n { 'data-component': presentation.component, 'data-theme': options.theme },\n rendered as ReactNode,\n ),\n );\n }\n async function stylesheet() {\n return typeof options.stylesheet === 'object'\n ? readFile(options.stylesheet.path, 'utf8')\n : (options.stylesheet ?? '');\n }\n async function html(presentation: ComponentPresentation<object>): Promise<string> {\n return document(await content(presentation));\n }\n async function document(markup: string) {\n const css = await stylesheet();\n // Closing tags in trusted CSS must still never break out of the style element.\n return `<!doctype html><html><head><meta charset=\"utf-8\"><style>${css.replace(/<\\/style/gi, '<\\\\/style')}</style></head><body>${markup}</body></html>`;\n }\n async function pages(presentation: ComponentPresentation<object>, settings: ServerRenderOptions) {\n const limits = boundedCaptureOptions(settings);\n const size = settings.rowsPerPage ?? 30;\n if (!Number.isInteger(size) || size < 1 || size > 500)\n throw new RangeError('genui: rowsPerPage must be 1–500');\n const capture = settings.capture ?? options.capture;\n if (!capture) throw new Error('genui: a capture adapter is required for PNG/PDF output');\n const rendered = await options.registry.renderPages(presentation, size);\n return {\n html: await Promise.all(\n rendered.map((node) =>\n document(\n renderToStaticMarkup(\n createElement(\n 'main',\n { 'data-component': presentation.component, 'data-theme': options.theme },\n node,\n ),\n ),\n ),\n ),\n ),\n capture,\n limits,\n };\n }\n return {\n html,\n async images(presentation: ComponentPresentation<object>, settings: ServerRenderOptions = {}) {\n const page = await pages(presentation, settings);\n return page.capture.images(page.html, page.limits);\n },\n async pdf(presentation: ComponentPresentation<object>, settings: ServerRenderOptions = {}) {\n const page = await pages(presentation, settings);\n return page.capture.pdf(page.html, page.limits);\n },\n };\n}\n","import type { Browser, LaunchOptions, Page } from 'playwright-core';\nimport { type CaptureAdapter, type CaptureOptions, boundedCaptureOptions } from './server.js';\n\nexport interface PlaywrightCaptureOptions {\n /** Caller-owned browser; the adapter closes only its own contexts and pages. */\n browser?: Browser;\n /** Used only without a supplied browser. Install playwright-core and supply executablePath. */\n launchOptions?: LaunchOptions;\n /** Explicit allowlist for trusted app images/fonts/styles. Redirects and other requests abort. */\n allowAsset?: (url: string) => boolean;\n}\nexport function createPlaywrightCaptureAdapter(\n options: PlaywrightCaptureOptions = {},\n): CaptureAdapter {\n async function withPage<T>(\n settings: CaptureOptions,\n operation: (page: Page, limits: Required<CaptureOptions>) => Promise<T>,\n ) {\n const limits = boundedCaptureOptions(settings);\n const browser =\n options.browser ??\n (await (await import('playwright-core')).chromium.launch(options.launchOptions));\n try {\n const context = await browser.newContext({\n javaScriptEnabled: false,\n serviceWorkers: 'block',\n viewport: { width: limits.width, height: limits.height },\n });\n try {\n const page = await context.newPage();\n try {\n page.setDefaultTimeout(limits.timeoutMs);\n await context.route('**/*', async (route) => {\n try {\n if (!options.allowAsset?.(route.request().url())) {\n await route.abort();\n return;\n }\n // Browser redirects bypass route handlers. Fetch one approved URL only,\n // then fulfill its response; never let an asset redirect escape the allowlist.\n const response = await route.fetch({ maxRedirects: 0, timeout: limits.timeoutMs });\n try {\n if (response.status() >= 300 && response.status() < 400) {\n await route.abort();\n } else {\n await route.fulfill({ response });\n }\n } finally {\n await response.dispose();\n }\n } catch {\n await route.abort();\n }\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n operation(page, limits),\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error('genui: capture timed out')),\n limits.timeoutMs,\n );\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n } finally {\n await page.close();\n }\n } finally {\n await context.close();\n }\n } finally {\n if (!options.browser) await browser.close();\n }\n }\n function checkPages(html: readonly string[]) {\n if (!html.length || html.length > 100)\n throw new RangeError('genui: capture requires 1–100 pages');\n }\n async function ready(page: Page, html: string, limits: Required<CaptureOptions>) {\n await page.setContent(html, { waitUntil: 'load', timeout: limits.timeoutMs });\n // Host evaluation is available while page-origin JavaScript stays disabled.\n await page.evaluate(async (timeoutMs) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n Promise.all([\n document.fonts.ready,\n ...Array.from(document.images, (image) =>\n image.complete\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n image.addEventListener('load', () => resolve(), { once: true });\n image.addEventListener('error', () => resolve(), { once: true });\n }),\n ),\n ]),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('genui: assets timed out')), timeoutMs);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n }, limits.timeoutMs);\n const height = await page.evaluate(() =>\n Math.max(document.body.scrollHeight, document.documentElement.scrollHeight),\n );\n if (height > limits.height)\n throw new RangeError(\n 'genui: content exceeds capture height; reduce rowsPerPage or paginate the component',\n );\n }\n return {\n async images(html, settings) {\n checkPages(html);\n return withPage(settings, async (page, limits) => {\n const images: Uint8Array[] = [];\n for (const content of html) {\n await ready(page, content, limits);\n images.push(\n await page.screenshot({ type: 'png', fullPage: true, timeout: limits.timeoutMs }),\n );\n }\n return images;\n });\n },\n async pdf(html, settings) {\n checkPages(html);\n return withPage(settings, async (page, limits) => {\n // Separate full HTML pages via srcdoc to preserve per-page CSS and theme.\n // Keep frames in the intercepted origin; opaque sandbox frames can bypass Chromium\n // request routing. Scripts remain disabled by both the context and the sandbox.\n const escapeAttribute = (value: string) =>\n value.replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<');\n const pdfDocument = `<!doctype html><html><head><style>@page { size: ${limits.width}px ${limits.height}px; margin: 0 } body { margin: 0 } iframe { display:block; border:0; width:${limits.width}px; height:${limits.height}px; break-after:page } iframe:last-child { break-after:auto }</style></head><body>${html.map((content) => `<iframe sandbox=\"allow-same-origin\" srcdoc=\"${escapeAttribute(content)}\"></iframe>`).join('')}</body></html>`;\n await page.setContent(pdfDocument, { waitUntil: 'load', timeout: limits.timeoutMs });\n for (const frame of page.frames().slice(1)) {\n await frame.evaluate(async () => {\n await document.fonts.ready;\n await Promise.all(\n Array.from(document.images, (image) => image.decode().catch(() => {})),\n );\n });\n const height = await frame.evaluate(() => document.documentElement.scrollHeight);\n if (height > limits.height)\n throw new RangeError('genui: content exceeds PDF page height; reduce rowsPerPage');\n }\n return page.pdf({\n width: `${limits.width}px`,\n height: `${limits.height}px`,\n printBackground: true,\n preferCSSPageSize: true,\n });\n });\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/genui/server.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
|
+
boundedCaptureOptions: () => boundedCaptureOptions,
|
|
24
|
+
createReactComponentRegistry: () => createReactComponentRegistry,
|
|
25
|
+
createReactServerRenderer: () => createReactServerRenderer
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(server_exports);
|
|
28
|
+
var import_promises = require("fs/promises");
|
|
29
|
+
var import_react2 = require("react");
|
|
30
|
+
var import_server = require("react-dom/server");
|
|
31
|
+
|
|
32
|
+
// src/genui/react-registry.ts
|
|
33
|
+
var import_genui = require("@dudousxd/nestjs-agent-core/genui");
|
|
34
|
+
var import_react = require("react");
|
|
35
|
+
function createReactComponentRegistry(options) {
|
|
36
|
+
const registry = (0, import_genui.createComponentRegistry)(options);
|
|
37
|
+
const components = /* @__PURE__ */ Object.create(null);
|
|
38
|
+
const texts = /* @__PURE__ */ new Map();
|
|
39
|
+
const paginators = /* @__PURE__ */ new Map();
|
|
40
|
+
const adapter = {
|
|
41
|
+
get catalog() {
|
|
42
|
+
return registry.catalog;
|
|
43
|
+
},
|
|
44
|
+
get manifest() {
|
|
45
|
+
return registry.manifest;
|
|
46
|
+
},
|
|
47
|
+
get components() {
|
|
48
|
+
return { ...components };
|
|
49
|
+
},
|
|
50
|
+
prepare: (presentation) => registry.prepare({ ...presentation, props: presentation.props }),
|
|
51
|
+
render: (presentation, channel) => registry.render(
|
|
52
|
+
{ ...presentation, props: presentation.props },
|
|
53
|
+
channel
|
|
54
|
+
),
|
|
55
|
+
register(definition, renderers) {
|
|
56
|
+
const ReactComponent = renderers.react;
|
|
57
|
+
registry.register(definition, {
|
|
58
|
+
...ReactComponent ? { react: (props) => (0, import_react.createElement)(ReactComponent, props) } : {},
|
|
59
|
+
...renderers.text ? { text: renderers.text } : {}
|
|
60
|
+
});
|
|
61
|
+
if (renderers.react) components[definition.name] = renderers.react;
|
|
62
|
+
if (renderers.text) {
|
|
63
|
+
const text = renderers.text;
|
|
64
|
+
texts.set(definition.name, (props) => text(props));
|
|
65
|
+
}
|
|
66
|
+
if (renderers.paginate) {
|
|
67
|
+
const paginate = renderers.paginate;
|
|
68
|
+
paginators.set(
|
|
69
|
+
definition.name,
|
|
70
|
+
async (props, size) => await paginate(props, size)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return adapter;
|
|
74
|
+
},
|
|
75
|
+
async renderPages(presentation, size) {
|
|
76
|
+
const pages = await adapter.paginate(presentation, size);
|
|
77
|
+
return Promise.all(
|
|
78
|
+
pages.map(async (page) => {
|
|
79
|
+
const ReactComponent = components[page.component];
|
|
80
|
+
if (ReactComponent) return (0, import_react.createElement)(ReactComponent, page.props);
|
|
81
|
+
return texts.get(page.component)?.(page.props) ?? page.fallbackText;
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
async paginate(presentation, size) {
|
|
86
|
+
if (!Number.isInteger(size) || size < 1 || size > 500)
|
|
87
|
+
throw new RangeError("genui: rowsPerPage must be 1\u2013500");
|
|
88
|
+
const prepared = await registry.prepare({
|
|
89
|
+
...presentation,
|
|
90
|
+
props: presentation.props
|
|
91
|
+
});
|
|
92
|
+
const custom = paginators.get(prepared.component);
|
|
93
|
+
const rows = prepared.props.rows;
|
|
94
|
+
const pages = custom ? await custom(prepared.props, size) : prepared.component === "DataTable" && Array.isArray(rows) && rows.length > size ? Array.from({ length: Math.ceil(rows.length / size) }, (_, index) => ({
|
|
95
|
+
...prepared.props,
|
|
96
|
+
rows: rows.slice(index * size, (index + 1) * size)
|
|
97
|
+
})) : [prepared.props];
|
|
98
|
+
if (!pages.length || pages.length > 100)
|
|
99
|
+
throw new RangeError("genui: capture requires 1\u2013100 pages");
|
|
100
|
+
return Promise.all(pages.map((props) => registry.prepare({ ...prepared, props })));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
return adapter;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/genui/server.ts
|
|
107
|
+
function boundedCaptureOptions(options) {
|
|
108
|
+
const result = {
|
|
109
|
+
width: options.width ?? 1200,
|
|
110
|
+
height: options.height ?? 1600,
|
|
111
|
+
timeoutMs: options.timeoutMs ?? 15e3
|
|
112
|
+
};
|
|
113
|
+
for (const [key, value, min, max] of [
|
|
114
|
+
["width", result.width, 320, 4096],
|
|
115
|
+
["height", result.height, 200, 16384],
|
|
116
|
+
["timeoutMs", result.timeoutMs, 100, 6e4]
|
|
117
|
+
]) {
|
|
118
|
+
if (!Number.isInteger(value) || value < min || value > max)
|
|
119
|
+
throw new RangeError(`genui: ${key} must be ${min}\u2013${max}`);
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
function createReactServerRenderer(options) {
|
|
124
|
+
async function content(presentation) {
|
|
125
|
+
const rendered = await options.registry.render(presentation, "react");
|
|
126
|
+
return (0, import_server.renderToStaticMarkup)(
|
|
127
|
+
(0, import_react2.createElement)(
|
|
128
|
+
"main",
|
|
129
|
+
{ "data-component": presentation.component, "data-theme": options.theme },
|
|
130
|
+
rendered
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
async function stylesheet() {
|
|
135
|
+
return typeof options.stylesheet === "object" ? (0, import_promises.readFile)(options.stylesheet.path, "utf8") : options.stylesheet ?? "";
|
|
136
|
+
}
|
|
137
|
+
async function html(presentation) {
|
|
138
|
+
return document(await content(presentation));
|
|
139
|
+
}
|
|
140
|
+
async function document(markup) {
|
|
141
|
+
const css = await stylesheet();
|
|
142
|
+
return `<!doctype html><html><head><meta charset="utf-8"><style>${css.replace(/<\/style/gi, "<\\/style")}</style></head><body>${markup}</body></html>`;
|
|
143
|
+
}
|
|
144
|
+
async function pages(presentation, settings) {
|
|
145
|
+
const limits = boundedCaptureOptions(settings);
|
|
146
|
+
const size = settings.rowsPerPage ?? 30;
|
|
147
|
+
if (!Number.isInteger(size) || size < 1 || size > 500)
|
|
148
|
+
throw new RangeError("genui: rowsPerPage must be 1\u2013500");
|
|
149
|
+
const capture = settings.capture ?? options.capture;
|
|
150
|
+
if (!capture) throw new Error("genui: a capture adapter is required for PNG/PDF output");
|
|
151
|
+
const rendered = await options.registry.renderPages(presentation, size);
|
|
152
|
+
return {
|
|
153
|
+
html: await Promise.all(
|
|
154
|
+
rendered.map(
|
|
155
|
+
(node) => document(
|
|
156
|
+
(0, import_server.renderToStaticMarkup)(
|
|
157
|
+
(0, import_react2.createElement)(
|
|
158
|
+
"main",
|
|
159
|
+
{ "data-component": presentation.component, "data-theme": options.theme },
|
|
160
|
+
node
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
),
|
|
166
|
+
capture,
|
|
167
|
+
limits
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
html,
|
|
172
|
+
async images(presentation, settings = {}) {
|
|
173
|
+
const page = await pages(presentation, settings);
|
|
174
|
+
return page.capture.images(page.html, page.limits);
|
|
175
|
+
},
|
|
176
|
+
async pdf(presentation, settings = {}) {
|
|
177
|
+
const page = await pages(presentation, settings);
|
|
178
|
+
return page.capture.pdf(page.html, page.limits);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
183
|
+
0 && (module.exports = {
|
|
184
|
+
boundedCaptureOptions,
|
|
185
|
+
createReactComponentRegistry,
|
|
186
|
+
createReactServerRenderer
|
|
187
|
+
});
|
|
188
|
+
//# sourceMappingURL=genui-server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/genui/server.ts","../src/genui/react-registry.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ComponentPresentation } from '@dudousxd/nestjs-agent-core/genui';\nimport { type ReactNode, createElement } from 'react';\nimport { renderToStaticMarkup } from 'react-dom/server';\nimport type { ReactComponentRegistry } from './react-registry.js';\nexport { createReactComponentRegistry, type ReactComponentRegistry } from './react-registry.js';\n\nexport interface CaptureOptions {\n width?: number;\n height?: number;\n timeoutMs?: number;\n}\nexport interface CaptureAdapter {\n images(html: readonly string[], options: CaptureOptions): Promise<Uint8Array[]>;\n pdf(html: readonly string[], options: CaptureOptions): Promise<Uint8Array>;\n}\nexport interface ServerRenderOptions extends CaptureOptions {\n rowsPerPage?: number;\n capture?: CaptureAdapter;\n}\nexport interface ReactServerRendererOptions {\n registry: ReactComponentRegistry;\n /** Trusted app CSS, or a trusted filesystem path; never supply user CSS. */\n stylesheet?: string | { path: string };\n theme?: string;\n capture?: CaptureAdapter;\n}\nexport function boundedCaptureOptions(options: CaptureOptions): Required<CaptureOptions> {\n const result = {\n width: options.width ?? 1200,\n height: options.height ?? 1600,\n timeoutMs: options.timeoutMs ?? 15000,\n };\n for (const [key, value, min, max] of [\n ['width', result.width, 320, 4096],\n ['height', result.height, 200, 16384],\n ['timeoutMs', result.timeoutMs, 100, 60000],\n ] as const) {\n if (!Number.isInteger(value) || value < min || value > max)\n throw new RangeError(`genui: ${key} must be ${min}–${max}`);\n }\n return result;\n}\nexport function createReactServerRenderer(options: ReactServerRendererOptions) {\n async function content(presentation: ComponentPresentation<object>) {\n const rendered = await options.registry.render(presentation, 'react');\n return renderToStaticMarkup(\n createElement(\n 'main',\n { 'data-component': presentation.component, 'data-theme': options.theme },\n rendered as ReactNode,\n ),\n );\n }\n async function stylesheet() {\n return typeof options.stylesheet === 'object'\n ? readFile(options.stylesheet.path, 'utf8')\n : (options.stylesheet ?? '');\n }\n async function html(presentation: ComponentPresentation<object>): Promise<string> {\n return document(await content(presentation));\n }\n async function document(markup: string) {\n const css = await stylesheet();\n // Closing tags in trusted CSS must still never break out of the style element.\n return `<!doctype html><html><head><meta charset=\"utf-8\"><style>${css.replace(/<\\/style/gi, '<\\\\/style')}</style></head><body>${markup}</body></html>`;\n }\n async function pages(presentation: ComponentPresentation<object>, settings: ServerRenderOptions) {\n const limits = boundedCaptureOptions(settings);\n const size = settings.rowsPerPage ?? 30;\n if (!Number.isInteger(size) || size < 1 || size > 500)\n throw new RangeError('genui: rowsPerPage must be 1–500');\n const capture = settings.capture ?? options.capture;\n if (!capture) throw new Error('genui: a capture adapter is required for PNG/PDF output');\n const rendered = await options.registry.renderPages(presentation, size);\n return {\n html: await Promise.all(\n rendered.map((node) =>\n document(\n renderToStaticMarkup(\n createElement(\n 'main',\n { 'data-component': presentation.component, 'data-theme': options.theme },\n node,\n ),\n ),\n ),\n ),\n ),\n capture,\n limits,\n };\n }\n return {\n html,\n async images(presentation: ComponentPresentation<object>, settings: ServerRenderOptions = {}) {\n const page = await pages(presentation, settings);\n return page.capture.images(page.html, page.limits);\n },\n async pdf(presentation: ComponentPresentation<object>, settings: ServerRenderOptions = {}) {\n const page = await pages(presentation, settings);\n return page.capture.pdf(page.html, page.limits);\n },\n };\n}\n","import {\n type CatalogOptions,\n type ComponentDefinition,\n type ComponentPresentation,\n createComponentRegistry,\n} from '@dudousxd/nestjs-agent-core/genui';\nimport { type ComponentType, type ReactNode, createElement } from 'react';\nimport type { GenuiRegistry } from './types.js';\n\nexport interface ReactComponentRenderers<P extends object> {\n react?: ComponentType<P>;\n text?: (props: P) => string | Promise<string>;\n /** Return each page's complete props. Each page is validated again before SSR. */\n paginate?: (props: P, rowsPerPage: number) => readonly P[] | Promise<readonly P[]>;\n}\n\n/** App-scoped registry shared by GenuiProvider and static server rendering. */\nexport function createReactComponentRegistry(options?: CatalogOptions) {\n const registry = createComponentRegistry(options);\n const components: GenuiRegistry = Object.create(null);\n const texts = new Map<string, (props: Record<string, unknown>) => string | Promise<string>>();\n const paginators = new Map<\n string,\n (props: Record<string, unknown>, size: number) => Promise<readonly Record<string, unknown>[]>\n >();\n const adapter = {\n get catalog() {\n return registry.catalog;\n },\n get manifest() {\n return registry.manifest;\n },\n get components(): GenuiRegistry {\n return { ...components };\n },\n prepare: (presentation: ComponentPresentation<object>) =>\n registry.prepare({ ...presentation, props: presentation.props as Record<string, unknown> }),\n render: (presentation: ComponentPresentation<object>, channel: string) =>\n registry.render(\n { ...presentation, props: presentation.props as Record<string, unknown> },\n channel,\n ),\n register<P extends object>(\n definition: ComponentDefinition<P>,\n renderers: ReactComponentRenderers<P>,\n ) {\n const ReactComponent = renderers.react;\n registry.register(definition, {\n ...(ReactComponent ? { react: (props: P) => createElement(ReactComponent, props) } : {}),\n ...(renderers.text ? { text: renderers.text } : {}),\n });\n if (renderers.react) components[definition.name] = renderers.react;\n if (renderers.text) {\n const text = renderers.text;\n texts.set(definition.name, (props) => text(props as P));\n }\n if (renderers.paginate) {\n const paginate = renderers.paginate;\n paginators.set(\n definition.name,\n async (props, size) =>\n (await paginate(props as P, size)) as readonly Record<string, unknown>[],\n );\n }\n return adapter;\n },\n async renderPages(\n presentation: ComponentPresentation<object>,\n size: number,\n ): Promise<ReactNode[]> {\n const pages = await adapter.paginate(presentation, size);\n return Promise.all(\n pages.map(async (page) => {\n const ReactComponent = components[page.component];\n if (ReactComponent) return createElement(ReactComponent, page.props);\n return texts.get(page.component)?.(page.props) ?? page.fallbackText;\n }),\n );\n },\n async paginate(\n presentation: ComponentPresentation<object>,\n size: number,\n ): Promise<ComponentPresentation[]> {\n if (!Number.isInteger(size) || size < 1 || size > 500)\n throw new RangeError('genui: rowsPerPage must be 1–500');\n const prepared = await registry.prepare({\n ...presentation,\n props: presentation.props as Record<string, unknown>,\n });\n const custom = paginators.get(prepared.component);\n const rows = prepared.props.rows;\n const pages = custom\n ? await custom(prepared.props, size)\n : prepared.component === 'DataTable' && Array.isArray(rows) && rows.length > size\n ? Array.from({ length: Math.ceil(rows.length / size) }, (_, index) => ({\n ...prepared.props,\n rows: rows.slice(index * size, (index + 1) * size),\n }))\n : [prepared.props];\n if (!pages.length || pages.length > 100)\n throw new RangeError('genui: capture requires 1–100 pages');\n return Promise.all(pages.map((props) => registry.prepare({ ...prepared, props })));\n },\n };\n return adapter;\n}\nexport type ReactComponentRegistry = ReturnType<typeof createReactComponentRegistry>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAyB;AAEzB,IAAAA,gBAA8C;AAC9C,oBAAqC;;;ACHrC,mBAKO;AACP,mBAAkE;AAW3D,SAAS,6BAA6B,SAA0B;AACrE,QAAM,eAAW,sCAAwB,OAAO;AAChD,QAAM,aAA4B,uBAAO,OAAO,IAAI;AACpD,QAAM,QAAQ,oBAAI,IAA0E;AAC5F,QAAM,aAAa,oBAAI,IAGrB;AACF,QAAM,UAAU;AAAA,IACd,IAAI,UAAU;AACZ,aAAO,SAAS;AAAA,IAClB;AAAA,IACA,IAAI,WAAW;AACb,aAAO,SAAS;AAAA,IAClB;AAAA,IACA,IAAI,aAA4B;AAC9B,aAAO,EAAE,GAAG,WAAW;AAAA,IACzB;AAAA,IACA,SAAS,CAAC,iBACR,SAAS,QAAQ,EAAE,GAAG,cAAc,OAAO,aAAa,MAAiC,CAAC;AAAA,IAC5F,QAAQ,CAAC,cAA6C,YACpD,SAAS;AAAA,MACP,EAAE,GAAG,cAAc,OAAO,aAAa,MAAiC;AAAA,MACxE;AAAA,IACF;AAAA,IACF,SACE,YACA,WACA;AACA,YAAM,iBAAiB,UAAU;AACjC,eAAS,SAAS,YAAY;AAAA,QAC5B,GAAI,iBAAiB,EAAE,OAAO,CAAC,cAAa,4BAAc,gBAAgB,KAAK,EAAE,IAAI,CAAC;AAAA,QACtF,GAAI,UAAU,OAAO,EAAE,MAAM,UAAU,KAAK,IAAI,CAAC;AAAA,MACnD,CAAC;AACD,UAAI,UAAU,MAAO,YAAW,WAAW,IAAI,IAAI,UAAU;AAC7D,UAAI,UAAU,MAAM;AAClB,cAAM,OAAO,UAAU;AACvB,cAAM,IAAI,WAAW,MAAM,CAAC,UAAU,KAAK,KAAU,CAAC;AAAA,MACxD;AACA,UAAI,UAAU,UAAU;AACtB,cAAM,WAAW,UAAU;AAC3B,mBAAW;AAAA,UACT,WAAW;AAAA,UACX,OAAO,OAAO,SACX,MAAM,SAAS,OAAY,IAAI;AAAA,QACpC;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IACA,MAAM,YACJ,cACA,MACsB;AACtB,YAAM,QAAQ,MAAM,QAAQ,SAAS,cAAc,IAAI;AACvD,aAAO,QAAQ;AAAA,QACb,MAAM,IAAI,OAAO,SAAS;AACxB,gBAAM,iBAAiB,WAAW,KAAK,SAAS;AAChD,cAAI,eAAgB,YAAO,4BAAc,gBAAgB,KAAK,KAAK;AACnE,iBAAO,MAAM,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,KAAK;AAAA,QACzD,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,MAAM,SACJ,cACA,MACkC;AAClC,UAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO;AAChD,cAAM,IAAI,WAAW,uCAAkC;AACzD,YAAM,WAAW,MAAM,SAAS,QAAQ;AAAA,QACtC,GAAG;AAAA,QACH,OAAO,aAAa;AAAA,MACtB,CAAC;AACD,YAAM,SAAS,WAAW,IAAI,SAAS,SAAS;AAChD,YAAM,OAAO,SAAS,MAAM;AAC5B,YAAM,QAAQ,SACV,MAAM,OAAO,SAAS,OAAO,IAAI,IACjC,SAAS,cAAc,eAAe,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,OACzE,MAAM,KAAK,EAAE,QAAQ,KAAK,KAAK,KAAK,SAAS,IAAI,EAAE,GAAG,CAAC,GAAG,WAAW;AAAA,QACnE,GAAG,SAAS;AAAA,QACZ,MAAM,KAAK,MAAM,QAAQ,OAAO,QAAQ,KAAK,IAAI;AAAA,MACnD,EAAE,IACF,CAAC,SAAS,KAAK;AACrB,UAAI,CAAC,MAAM,UAAU,MAAM,SAAS;AAClC,cAAM,IAAI,WAAW,0CAAqC;AAC5D,aAAO,QAAQ,IAAI,MAAM,IAAI,CAAC,UAAU,SAAS,QAAQ,EAAE,GAAG,UAAU,MAAM,CAAC,CAAC,CAAC;AAAA,IACnF;AAAA,EACF;AACA,SAAO;AACT;;;AD9EO,SAAS,sBAAsB,SAAmD;AACvF,QAAM,SAAS;AAAA,IACb,OAAO,QAAQ,SAAS;AAAA,IACxB,QAAQ,QAAQ,UAAU;AAAA,IAC1B,WAAW,QAAQ,aAAa;AAAA,EAClC;AACA,aAAW,CAAC,KAAK,OAAO,KAAK,GAAG,KAAK;AAAA,IACnC,CAAC,SAAS,OAAO,OAAO,KAAK,IAAI;AAAA,IACjC,CAAC,UAAU,OAAO,QAAQ,KAAK,KAAK;AAAA,IACpC,CAAC,aAAa,OAAO,WAAW,KAAK,GAAK;AAAA,EAC5C,GAAY;AACV,QAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,OAAO,QAAQ;AACrD,YAAM,IAAI,WAAW,UAAU,GAAG,YAAY,GAAG,SAAI,GAAG,EAAE;AAAA,EAC9D;AACA,SAAO;AACT;AACO,SAAS,0BAA0B,SAAqC;AAC7E,iBAAe,QAAQ,cAA6C;AAClE,UAAM,WAAW,MAAM,QAAQ,SAAS,OAAO,cAAc,OAAO;AACpE,eAAO;AAAA,UACL;AAAA,QACE;AAAA,QACA,EAAE,kBAAkB,aAAa,WAAW,cAAc,QAAQ,MAAM;AAAA,QACxE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,iBAAe,aAAa;AAC1B,WAAO,OAAO,QAAQ,eAAe,eACjC,0BAAS,QAAQ,WAAW,MAAM,MAAM,IACvC,QAAQ,cAAc;AAAA,EAC7B;AACA,iBAAe,KAAK,cAA8D;AAChF,WAAO,SAAS,MAAM,QAAQ,YAAY,CAAC;AAAA,EAC7C;AACA,iBAAe,SAAS,QAAgB;AACtC,UAAM,MAAM,MAAM,WAAW;AAE7B,WAAO,2DAA2D,IAAI,QAAQ,cAAc,WAAW,CAAC,wBAAwB,MAAM;AAAA,EACxI;AACA,iBAAe,MAAM,cAA6C,UAA+B;AAC/F,UAAM,SAAS,sBAAsB,QAAQ;AAC7C,UAAM,OAAO,SAAS,eAAe;AACrC,QAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO;AAChD,YAAM,IAAI,WAAW,uCAAkC;AACzD,UAAM,UAAU,SAAS,WAAW,QAAQ;AAC5C,QAAI,CAAC,QAAS,OAAM,IAAI,MAAM,yDAAyD;AACvF,UAAM,WAAW,MAAM,QAAQ,SAAS,YAAY,cAAc,IAAI;AACtE,WAAO;AAAA,MACL,MAAM,MAAM,QAAQ;AAAA,QAClB,SAAS;AAAA,UAAI,CAAC,SACZ;AAAA,gBACE;AAAA,kBACE;AAAA,gBACE;AAAA,gBACA,EAAE,kBAAkB,aAAa,WAAW,cAAc,QAAQ,MAAM;AAAA,gBACxE;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA,MAAM,OAAO,cAA6C,WAAgC,CAAC,GAAG;AAC5F,YAAM,OAAO,MAAM,MAAM,cAAc,QAAQ;AAC/C,aAAO,KAAK,QAAQ,OAAO,KAAK,MAAM,KAAK,MAAM;AAAA,IACnD;AAAA,IACA,MAAM,IAAI,cAA6C,WAAgC,CAAC,GAAG;AACzF,YAAM,OAAO,MAAM,MAAM,cAAc,QAAQ;AAC/C,aAAO,KAAK,QAAQ,IAAI,KAAK,MAAM,KAAK,MAAM;AAAA,IAChD;AAAA,EACF;AACF;","names":["import_react"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ComponentPresentation } from '@dudousxd/nestjs-agent-core/genui';
|
|
2
|
+
import { R as ReactComponentRegistry } from './react-registry-Di4IVrw-.cjs';
|
|
3
|
+
export { c as createReactComponentRegistry } from './react-registry-Di4IVrw-.cjs';
|
|
4
|
+
import 'react';
|
|
5
|
+
import './types-DZtFsWec.cjs';
|
|
6
|
+
|
|
7
|
+
interface CaptureOptions {
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
interface CaptureAdapter {
|
|
13
|
+
images(html: readonly string[], options: CaptureOptions): Promise<Uint8Array[]>;
|
|
14
|
+
pdf(html: readonly string[], options: CaptureOptions): Promise<Uint8Array>;
|
|
15
|
+
}
|
|
16
|
+
interface ServerRenderOptions extends CaptureOptions {
|
|
17
|
+
rowsPerPage?: number;
|
|
18
|
+
capture?: CaptureAdapter;
|
|
19
|
+
}
|
|
20
|
+
interface ReactServerRendererOptions {
|
|
21
|
+
registry: ReactComponentRegistry;
|
|
22
|
+
/** Trusted app CSS, or a trusted filesystem path; never supply user CSS. */
|
|
23
|
+
stylesheet?: string | {
|
|
24
|
+
path: string;
|
|
25
|
+
};
|
|
26
|
+
theme?: string;
|
|
27
|
+
capture?: CaptureAdapter;
|
|
28
|
+
}
|
|
29
|
+
declare function boundedCaptureOptions(options: CaptureOptions): Required<CaptureOptions>;
|
|
30
|
+
declare function createReactServerRenderer(options: ReactServerRendererOptions): {
|
|
31
|
+
html: (presentation: ComponentPresentation<object>) => Promise<string>;
|
|
32
|
+
images(presentation: ComponentPresentation<object>, settings?: ServerRenderOptions): Promise<Uint8Array<ArrayBufferLike>[]>;
|
|
33
|
+
pdf(presentation: ComponentPresentation<object>, settings?: ServerRenderOptions): Promise<Uint8Array<ArrayBufferLike>>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { type CaptureAdapter, type CaptureOptions, ReactComponentRegistry, type ReactServerRendererOptions, type ServerRenderOptions, boundedCaptureOptions, createReactServerRenderer };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ComponentPresentation } from '@dudousxd/nestjs-agent-core/genui';
|
|
2
|
+
import { R as ReactComponentRegistry } from './react-registry-z9GFbTm9.js';
|
|
3
|
+
export { c as createReactComponentRegistry } from './react-registry-z9GFbTm9.js';
|
|
4
|
+
import 'react';
|
|
5
|
+
import './types-DZtFsWec.js';
|
|
6
|
+
|
|
7
|
+
interface CaptureOptions {
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
interface CaptureAdapter {
|
|
13
|
+
images(html: readonly string[], options: CaptureOptions): Promise<Uint8Array[]>;
|
|
14
|
+
pdf(html: readonly string[], options: CaptureOptions): Promise<Uint8Array>;
|
|
15
|
+
}
|
|
16
|
+
interface ServerRenderOptions extends CaptureOptions {
|
|
17
|
+
rowsPerPage?: number;
|
|
18
|
+
capture?: CaptureAdapter;
|
|
19
|
+
}
|
|
20
|
+
interface ReactServerRendererOptions {
|
|
21
|
+
registry: ReactComponentRegistry;
|
|
22
|
+
/** Trusted app CSS, or a trusted filesystem path; never supply user CSS. */
|
|
23
|
+
stylesheet?: string | {
|
|
24
|
+
path: string;
|
|
25
|
+
};
|
|
26
|
+
theme?: string;
|
|
27
|
+
capture?: CaptureAdapter;
|
|
28
|
+
}
|
|
29
|
+
declare function boundedCaptureOptions(options: CaptureOptions): Required<CaptureOptions>;
|
|
30
|
+
declare function createReactServerRenderer(options: ReactServerRendererOptions): {
|
|
31
|
+
html: (presentation: ComponentPresentation<object>) => Promise<string>;
|
|
32
|
+
images(presentation: ComponentPresentation<object>, settings?: ServerRenderOptions): Promise<Uint8Array<ArrayBufferLike>[]>;
|
|
33
|
+
pdf(presentation: ComponentPresentation<object>, settings?: ServerRenderOptions): Promise<Uint8Array<ArrayBufferLike>>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { type CaptureAdapter, type CaptureOptions, ReactComponentRegistry, type ReactServerRendererOptions, type ServerRenderOptions, boundedCaptureOptions, createReactServerRenderer };
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/genui/server.ts
|
|
2
|
+
import { readFile } from "fs/promises";
|
|
3
|
+
import { createElement as createElement2 } from "react";
|
|
4
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
5
|
+
|
|
6
|
+
// src/genui/react-registry.ts
|
|
7
|
+
import {
|
|
8
|
+
createComponentRegistry
|
|
9
|
+
} from "@dudousxd/nestjs-agent-core/genui";
|
|
10
|
+
import { createElement } from "react";
|
|
11
|
+
function createReactComponentRegistry(options) {
|
|
12
|
+
const registry = createComponentRegistry(options);
|
|
13
|
+
const components = /* @__PURE__ */ Object.create(null);
|
|
14
|
+
const texts = /* @__PURE__ */ new Map();
|
|
15
|
+
const paginators = /* @__PURE__ */ new Map();
|
|
16
|
+
const adapter = {
|
|
17
|
+
get catalog() {
|
|
18
|
+
return registry.catalog;
|
|
19
|
+
},
|
|
20
|
+
get manifest() {
|
|
21
|
+
return registry.manifest;
|
|
22
|
+
},
|
|
23
|
+
get components() {
|
|
24
|
+
return { ...components };
|
|
25
|
+
},
|
|
26
|
+
prepare: (presentation) => registry.prepare({ ...presentation, props: presentation.props }),
|
|
27
|
+
render: (presentation, channel) => registry.render(
|
|
28
|
+
{ ...presentation, props: presentation.props },
|
|
29
|
+
channel
|
|
30
|
+
),
|
|
31
|
+
register(definition, renderers) {
|
|
32
|
+
const ReactComponent = renderers.react;
|
|
33
|
+
registry.register(definition, {
|
|
34
|
+
...ReactComponent ? { react: (props) => createElement(ReactComponent, props) } : {},
|
|
35
|
+
...renderers.text ? { text: renderers.text } : {}
|
|
36
|
+
});
|
|
37
|
+
if (renderers.react) components[definition.name] = renderers.react;
|
|
38
|
+
if (renderers.text) {
|
|
39
|
+
const text = renderers.text;
|
|
40
|
+
texts.set(definition.name, (props) => text(props));
|
|
41
|
+
}
|
|
42
|
+
if (renderers.paginate) {
|
|
43
|
+
const paginate = renderers.paginate;
|
|
44
|
+
paginators.set(
|
|
45
|
+
definition.name,
|
|
46
|
+
async (props, size) => await paginate(props, size)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return adapter;
|
|
50
|
+
},
|
|
51
|
+
async renderPages(presentation, size) {
|
|
52
|
+
const pages = await adapter.paginate(presentation, size);
|
|
53
|
+
return Promise.all(
|
|
54
|
+
pages.map(async (page) => {
|
|
55
|
+
const ReactComponent = components[page.component];
|
|
56
|
+
if (ReactComponent) return createElement(ReactComponent, page.props);
|
|
57
|
+
return texts.get(page.component)?.(page.props) ?? page.fallbackText;
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
async paginate(presentation, size) {
|
|
62
|
+
if (!Number.isInteger(size) || size < 1 || size > 500)
|
|
63
|
+
throw new RangeError("genui: rowsPerPage must be 1\u2013500");
|
|
64
|
+
const prepared = await registry.prepare({
|
|
65
|
+
...presentation,
|
|
66
|
+
props: presentation.props
|
|
67
|
+
});
|
|
68
|
+
const custom = paginators.get(prepared.component);
|
|
69
|
+
const rows = prepared.props.rows;
|
|
70
|
+
const pages = custom ? await custom(prepared.props, size) : prepared.component === "DataTable" && Array.isArray(rows) && rows.length > size ? Array.from({ length: Math.ceil(rows.length / size) }, (_, index) => ({
|
|
71
|
+
...prepared.props,
|
|
72
|
+
rows: rows.slice(index * size, (index + 1) * size)
|
|
73
|
+
})) : [prepared.props];
|
|
74
|
+
if (!pages.length || pages.length > 100)
|
|
75
|
+
throw new RangeError("genui: capture requires 1\u2013100 pages");
|
|
76
|
+
return Promise.all(pages.map((props) => registry.prepare({ ...prepared, props })));
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
return adapter;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/genui/server.ts
|
|
83
|
+
function boundedCaptureOptions(options) {
|
|
84
|
+
const result = {
|
|
85
|
+
width: options.width ?? 1200,
|
|
86
|
+
height: options.height ?? 1600,
|
|
87
|
+
timeoutMs: options.timeoutMs ?? 15e3
|
|
88
|
+
};
|
|
89
|
+
for (const [key, value, min, max] of [
|
|
90
|
+
["width", result.width, 320, 4096],
|
|
91
|
+
["height", result.height, 200, 16384],
|
|
92
|
+
["timeoutMs", result.timeoutMs, 100, 6e4]
|
|
93
|
+
]) {
|
|
94
|
+
if (!Number.isInteger(value) || value < min || value > max)
|
|
95
|
+
throw new RangeError(`genui: ${key} must be ${min}\u2013${max}`);
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
function createReactServerRenderer(options) {
|
|
100
|
+
async function content(presentation) {
|
|
101
|
+
const rendered = await options.registry.render(presentation, "react");
|
|
102
|
+
return renderToStaticMarkup(
|
|
103
|
+
createElement2(
|
|
104
|
+
"main",
|
|
105
|
+
{ "data-component": presentation.component, "data-theme": options.theme },
|
|
106
|
+
rendered
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
async function stylesheet() {
|
|
111
|
+
return typeof options.stylesheet === "object" ? readFile(options.stylesheet.path, "utf8") : options.stylesheet ?? "";
|
|
112
|
+
}
|
|
113
|
+
async function html(presentation) {
|
|
114
|
+
return document(await content(presentation));
|
|
115
|
+
}
|
|
116
|
+
async function document(markup) {
|
|
117
|
+
const css = await stylesheet();
|
|
118
|
+
return `<!doctype html><html><head><meta charset="utf-8"><style>${css.replace(/<\/style/gi, "<\\/style")}</style></head><body>${markup}</body></html>`;
|
|
119
|
+
}
|
|
120
|
+
async function pages(presentation, settings) {
|
|
121
|
+
const limits = boundedCaptureOptions(settings);
|
|
122
|
+
const size = settings.rowsPerPage ?? 30;
|
|
123
|
+
if (!Number.isInteger(size) || size < 1 || size > 500)
|
|
124
|
+
throw new RangeError("genui: rowsPerPage must be 1\u2013500");
|
|
125
|
+
const capture = settings.capture ?? options.capture;
|
|
126
|
+
if (!capture) throw new Error("genui: a capture adapter is required for PNG/PDF output");
|
|
127
|
+
const rendered = await options.registry.renderPages(presentation, size);
|
|
128
|
+
return {
|
|
129
|
+
html: await Promise.all(
|
|
130
|
+
rendered.map(
|
|
131
|
+
(node) => document(
|
|
132
|
+
renderToStaticMarkup(
|
|
133
|
+
createElement2(
|
|
134
|
+
"main",
|
|
135
|
+
{ "data-component": presentation.component, "data-theme": options.theme },
|
|
136
|
+
node
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
),
|
|
142
|
+
capture,
|
|
143
|
+
limits
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
html,
|
|
148
|
+
async images(presentation, settings = {}) {
|
|
149
|
+
const page = await pages(presentation, settings);
|
|
150
|
+
return page.capture.images(page.html, page.limits);
|
|
151
|
+
},
|
|
152
|
+
async pdf(presentation, settings = {}) {
|
|
153
|
+
const page = await pages(presentation, settings);
|
|
154
|
+
return page.capture.pdf(page.html, page.limits);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
boundedCaptureOptions,
|
|
160
|
+
createReactComponentRegistry,
|
|
161
|
+
createReactServerRenderer
|
|
162
|
+
};
|
|
163
|
+
//# sourceMappingURL=genui-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/genui/server.ts","../src/genui/react-registry.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ComponentPresentation } from '@dudousxd/nestjs-agent-core/genui';\nimport { type ReactNode, createElement } from 'react';\nimport { renderToStaticMarkup } from 'react-dom/server';\nimport type { ReactComponentRegistry } from './react-registry.js';\nexport { createReactComponentRegistry, type ReactComponentRegistry } from './react-registry.js';\n\nexport interface CaptureOptions {\n width?: number;\n height?: number;\n timeoutMs?: number;\n}\nexport interface CaptureAdapter {\n images(html: readonly string[], options: CaptureOptions): Promise<Uint8Array[]>;\n pdf(html: readonly string[], options: CaptureOptions): Promise<Uint8Array>;\n}\nexport interface ServerRenderOptions extends CaptureOptions {\n rowsPerPage?: number;\n capture?: CaptureAdapter;\n}\nexport interface ReactServerRendererOptions {\n registry: ReactComponentRegistry;\n /** Trusted app CSS, or a trusted filesystem path; never supply user CSS. */\n stylesheet?: string | { path: string };\n theme?: string;\n capture?: CaptureAdapter;\n}\nexport function boundedCaptureOptions(options: CaptureOptions): Required<CaptureOptions> {\n const result = {\n width: options.width ?? 1200,\n height: options.height ?? 1600,\n timeoutMs: options.timeoutMs ?? 15000,\n };\n for (const [key, value, min, max] of [\n ['width', result.width, 320, 4096],\n ['height', result.height, 200, 16384],\n ['timeoutMs', result.timeoutMs, 100, 60000],\n ] as const) {\n if (!Number.isInteger(value) || value < min || value > max)\n throw new RangeError(`genui: ${key} must be ${min}–${max}`);\n }\n return result;\n}\nexport function createReactServerRenderer(options: ReactServerRendererOptions) {\n async function content(presentation: ComponentPresentation<object>) {\n const rendered = await options.registry.render(presentation, 'react');\n return renderToStaticMarkup(\n createElement(\n 'main',\n { 'data-component': presentation.component, 'data-theme': options.theme },\n rendered as ReactNode,\n ),\n );\n }\n async function stylesheet() {\n return typeof options.stylesheet === 'object'\n ? readFile(options.stylesheet.path, 'utf8')\n : (options.stylesheet ?? '');\n }\n async function html(presentation: ComponentPresentation<object>): Promise<string> {\n return document(await content(presentation));\n }\n async function document(markup: string) {\n const css = await stylesheet();\n // Closing tags in trusted CSS must still never break out of the style element.\n return `<!doctype html><html><head><meta charset=\"utf-8\"><style>${css.replace(/<\\/style/gi, '<\\\\/style')}</style></head><body>${markup}</body></html>`;\n }\n async function pages(presentation: ComponentPresentation<object>, settings: ServerRenderOptions) {\n const limits = boundedCaptureOptions(settings);\n const size = settings.rowsPerPage ?? 30;\n if (!Number.isInteger(size) || size < 1 || size > 500)\n throw new RangeError('genui: rowsPerPage must be 1–500');\n const capture = settings.capture ?? options.capture;\n if (!capture) throw new Error('genui: a capture adapter is required for PNG/PDF output');\n const rendered = await options.registry.renderPages(presentation, size);\n return {\n html: await Promise.all(\n rendered.map((node) =>\n document(\n renderToStaticMarkup(\n createElement(\n 'main',\n { 'data-component': presentation.component, 'data-theme': options.theme },\n node,\n ),\n ),\n ),\n ),\n ),\n capture,\n limits,\n };\n }\n return {\n html,\n async images(presentation: ComponentPresentation<object>, settings: ServerRenderOptions = {}) {\n const page = await pages(presentation, settings);\n return page.capture.images(page.html, page.limits);\n },\n async pdf(presentation: ComponentPresentation<object>, settings: ServerRenderOptions = {}) {\n const page = await pages(presentation, settings);\n return page.capture.pdf(page.html, page.limits);\n },\n };\n}\n","import {\n type CatalogOptions,\n type ComponentDefinition,\n type ComponentPresentation,\n createComponentRegistry,\n} from '@dudousxd/nestjs-agent-core/genui';\nimport { type ComponentType, type ReactNode, createElement } from 'react';\nimport type { GenuiRegistry } from './types.js';\n\nexport interface ReactComponentRenderers<P extends object> {\n react?: ComponentType<P>;\n text?: (props: P) => string | Promise<string>;\n /** Return each page's complete props. Each page is validated again before SSR. */\n paginate?: (props: P, rowsPerPage: number) => readonly P[] | Promise<readonly P[]>;\n}\n\n/** App-scoped registry shared by GenuiProvider and static server rendering. */\nexport function createReactComponentRegistry(options?: CatalogOptions) {\n const registry = createComponentRegistry(options);\n const components: GenuiRegistry = Object.create(null);\n const texts = new Map<string, (props: Record<string, unknown>) => string | Promise<string>>();\n const paginators = new Map<\n string,\n (props: Record<string, unknown>, size: number) => Promise<readonly Record<string, unknown>[]>\n >();\n const adapter = {\n get catalog() {\n return registry.catalog;\n },\n get manifest() {\n return registry.manifest;\n },\n get components(): GenuiRegistry {\n return { ...components };\n },\n prepare: (presentation: ComponentPresentation<object>) =>\n registry.prepare({ ...presentation, props: presentation.props as Record<string, unknown> }),\n render: (presentation: ComponentPresentation<object>, channel: string) =>\n registry.render(\n { ...presentation, props: presentation.props as Record<string, unknown> },\n channel,\n ),\n register<P extends object>(\n definition: ComponentDefinition<P>,\n renderers: ReactComponentRenderers<P>,\n ) {\n const ReactComponent = renderers.react;\n registry.register(definition, {\n ...(ReactComponent ? { react: (props: P) => createElement(ReactComponent, props) } : {}),\n ...(renderers.text ? { text: renderers.text } : {}),\n });\n if (renderers.react) components[definition.name] = renderers.react;\n if (renderers.text) {\n const text = renderers.text;\n texts.set(definition.name, (props) => text(props as P));\n }\n if (renderers.paginate) {\n const paginate = renderers.paginate;\n paginators.set(\n definition.name,\n async (props, size) =>\n (await paginate(props as P, size)) as readonly Record<string, unknown>[],\n );\n }\n return adapter;\n },\n async renderPages(\n presentation: ComponentPresentation<object>,\n size: number,\n ): Promise<ReactNode[]> {\n const pages = await adapter.paginate(presentation, size);\n return Promise.all(\n pages.map(async (page) => {\n const ReactComponent = components[page.component];\n if (ReactComponent) return createElement(ReactComponent, page.props);\n return texts.get(page.component)?.(page.props) ?? page.fallbackText;\n }),\n );\n },\n async paginate(\n presentation: ComponentPresentation<object>,\n size: number,\n ): Promise<ComponentPresentation[]> {\n if (!Number.isInteger(size) || size < 1 || size > 500)\n throw new RangeError('genui: rowsPerPage must be 1–500');\n const prepared = await registry.prepare({\n ...presentation,\n props: presentation.props as Record<string, unknown>,\n });\n const custom = paginators.get(prepared.component);\n const rows = prepared.props.rows;\n const pages = custom\n ? await custom(prepared.props, size)\n : prepared.component === 'DataTable' && Array.isArray(rows) && rows.length > size\n ? Array.from({ length: Math.ceil(rows.length / size) }, (_, index) => ({\n ...prepared.props,\n rows: rows.slice(index * size, (index + 1) * size),\n }))\n : [prepared.props];\n if (!pages.length || pages.length > 100)\n throw new RangeError('genui: capture requires 1–100 pages');\n return Promise.all(pages.map((props) => registry.prepare({ ...prepared, props })));\n },\n };\n return adapter;\n}\nexport type ReactComponentRegistry = ReturnType<typeof createReactComponentRegistry>;\n"],"mappings":"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","names":["createElement","createElement"]}
|