@dooboostore/simple-boot-http-server-ssr 1.0.15 → 1.0.17
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 +98 -0
- package/dist/cjs/SimpleBootHttpSSRServer.js.map +2 -2
- package/dist/cjs/bundle-entry.js +46 -14
- package/dist/cjs/bundle-entry.js.map +2 -2
- package/dist/cjs/filters/SSRDomParserFilter.js +4 -3
- package/dist/cjs/filters/SSRDomParserFilter.js.map +2 -2
- package/dist/cjs/filters/SSRFilter.js +4 -3
- package/dist/cjs/filters/SSRFilter.js.map +2 -2
- package/dist/cjs/filters/{SSRLinkDomDomFilter.js → SSRLinkDomFilter.js} +11 -10
- package/dist/cjs/filters/SSRLinkDomFilter.js.map +7 -0
- package/dist/cjs/filters/SSRSimpleWebComponentDomParserFilter.js +81 -0
- package/dist/cjs/filters/SSRSimpleWebComponentDomParserFilter.js.map +7 -0
- package/dist/cjs/filters/SSRSimpleWebComponentFilter.js +158 -28
- package/dist/cjs/filters/SSRSimpleWebComponentFilter.js.map +2 -2
- package/dist/cjs/filters/index.js +9 -3
- package/dist/cjs/filters/index.js.map +2 -2
- package/dist/cjs/index.js +0 -2
- package/dist/cjs/index.js.map +2 -2
- package/dist/esm/SimpleBootHttpSSRServer.js.map +2 -2
- package/dist/esm/bundle-entry.js +22 -7
- package/dist/esm/bundle-entry.js.map +2 -2
- package/dist/esm/filters/SSRDomParserFilter.js +5 -4
- package/dist/esm/filters/SSRDomParserFilter.js.map +2 -2
- package/dist/esm/filters/SSRFilter.js +4 -3
- package/dist/esm/filters/SSRFilter.js.map +2 -2
- package/dist/esm/filters/{SSRLinkDomDomFilter.js → SSRLinkDomFilter.js} +7 -6
- package/dist/esm/filters/SSRLinkDomFilter.js.map +7 -0
- package/dist/esm/filters/SSRSimpleWebComponentDomParserFilter.js +58 -0
- package/dist/esm/filters/SSRSimpleWebComponentDomParserFilter.js.map +7 -0
- package/dist/esm/filters/SSRSimpleWebComponentFilter.js +148 -28
- package/dist/esm/filters/SSRSimpleWebComponentFilter.js.map +2 -2
- package/dist/esm/filters/index.js +4 -1
- package/dist/esm/filters/index.js.map +2 -2
- package/dist/esm/index.js +0 -1
- package/dist/esm/index.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-boot-http-server-ssr.esm.js +57949 -20024
- package/dist/esm-bundle/dooboostore-simple-boot-http-server-ssr.esm.js.map +4 -4
- package/dist/types/SimpleBootHttpSSRServer.d.ts +1 -1
- package/dist/types/SimpleBootHttpSSRServer.d.ts.map +1 -1
- package/dist/types/bundle-entry.d.ts +11 -7
- package/dist/types/bundle-entry.d.ts.map +1 -1
- package/dist/types/filters/SSRDomParserFilter.d.ts +3 -3
- package/dist/types/filters/SSRDomParserFilter.d.ts.map +1 -1
- package/dist/types/filters/SSRFilter.d.ts.map +1 -1
- package/dist/types/filters/{SSRLinkDomDomFilter.d.ts → SSRLinkDomFilter.d.ts} +5 -5
- package/dist/types/filters/SSRLinkDomFilter.d.ts.map +1 -0
- package/dist/types/filters/SSRSimpleWebComponentDomParserFilter.d.ts +37 -0
- package/dist/types/filters/SSRSimpleWebComponentDomParserFilter.d.ts.map +1 -0
- package/dist/types/filters/SSRSimpleWebComponentFilter.d.ts +23 -3
- package/dist/types/filters/SSRSimpleWebComponentFilter.d.ts.map +1 -1
- package/dist/types/filters/index.d.ts +4 -1
- package/dist/types/filters/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-boot-http-server-ssr.umd.js +58543 -20621
- package/dist/umd-bundle/dooboostore-simple-boot-http-server-ssr.umd.js.map +4 -4
- package/package.json +20 -19
- package/src/SimpleBootHttpSSRServer.ts +2 -3
- package/src/bundle-entry.ts +16 -7
- package/src/filters/SSRDomParserFilter.ts +9 -8
- package/src/filters/SSRFilter.ts +4 -3
- package/src/filters/{SSRLinkDomDomFilter.ts → SSRLinkDomFilter.ts} +7 -6
- package/src/filters/SSRSimpleWebComponentDomParserFilter.ts +106 -0
- package/src/filters/SSRSimpleWebComponentFilter.ts +242 -39
- package/src/filters/index.ts +5 -2
- package/src/index.ts +0 -1
- package/README.MD +0 -167
- package/dist/cjs/decorators/LoadAroundBefore.js +0 -54
- package/dist/cjs/decorators/LoadAroundBefore.js.map +0 -7
- package/dist/cjs/decorators/SaveAroundAfter.js +0 -53
- package/dist/cjs/decorators/SaveAroundAfter.js.map +0 -7
- package/dist/cjs/decorators/index.js +0 -24
- package/dist/cjs/decorators/index.js.map +0 -7
- package/dist/cjs/filters/SSRLinkDomDomFilter.js.map +0 -7
- package/dist/esm/decorators/LoadAroundBefore.js +0 -31
- package/dist/esm/decorators/LoadAroundBefore.js.map +0 -7
- package/dist/esm/decorators/SaveAroundAfter.js +0 -29
- package/dist/esm/decorators/SaveAroundAfter.js.map +0 -7
- package/dist/esm/decorators/index.js +0 -3
- package/dist/esm/decorators/index.js.map +0 -7
- package/dist/esm/filters/SSRLinkDomDomFilter.js.map +0 -7
- package/dist/types/decorators/LoadAroundBefore.d.ts +0 -2
- package/dist/types/decorators/LoadAroundBefore.d.ts.map +0 -1
- package/dist/types/decorators/SaveAroundAfter.d.ts +0 -3
- package/dist/types/decorators/SaveAroundAfter.d.ts.map +0 -1
- package/dist/types/decorators/index.d.ts +0 -3
- package/dist/types/decorators/index.d.ts.map +0 -1
- package/dist/types/filters/SSRLinkDomDomFilter.d.ts.map +0 -1
- package/src/decorators/LoadAroundBefore.ts +0 -28
- package/src/decorators/SaveAroundAfter.ts +0 -27
- package/src/decorators/index.ts +0 -2
|
@@ -4,10 +4,9 @@ import { Filter } from '@dooboostore/simple-boot-http-server/filters/Filter';
|
|
|
4
4
|
import { Mimes } from '@dooboostore/simple-boot-http-server/codes/Mimes';
|
|
5
5
|
import { HttpStatus } from '@dooboostore/simple-boot-http-server/codes/HttpStatus';
|
|
6
6
|
import { SimpleBootHttpServer } from '@dooboostore/simple-boot-http-server/SimpleBootHttpServer';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {getElementConfig} from "@dooboostore/simple-web-component";
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import {HttpMethod} from "@dooboostore/simple-boot-http-server";
|
|
11
10
|
|
|
12
11
|
export type SWCSSRConfig = {
|
|
13
12
|
frontDistPath: string;
|
|
@@ -15,76 +14,280 @@ export type SWCSSRConfig = {
|
|
|
15
14
|
welcomUrl?: string;
|
|
16
15
|
ssrExcludeFilter?: (rr: RequestResponse) => boolean;
|
|
17
16
|
/**
|
|
17
|
+
* (Deprecated for Playwright)
|
|
18
18
|
* Function to register components for each request.
|
|
19
|
+
* Playwright evaluates real JS files loaded from your index.html,
|
|
20
|
+
* so component registration happens automatically in the browser context.
|
|
19
21
|
*/
|
|
20
22
|
registerComponents?: (window: any) => Promise<void> | void;
|
|
23
|
+
playwright?: {
|
|
24
|
+
waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'commit';
|
|
25
|
+
waitForSelector?: string;
|
|
26
|
+
waitForTimeout?: number;
|
|
27
|
+
timeout?: number;
|
|
28
|
+
ignoreTags?: string[];
|
|
29
|
+
};
|
|
21
30
|
};
|
|
22
31
|
|
|
23
32
|
/**
|
|
24
33
|
* SSR Filter specifically for Simple Web Component (SWC).
|
|
25
|
-
* It utilizes
|
|
34
|
+
* It utilizes Playwright to render the page robustly and perfectly matching browser specs.
|
|
35
|
+
*
|
|
36
|
+
* ### Playwright Setup & Requirements
|
|
37
|
+
*
|
|
38
|
+
* To use this filter, Playwright must be installed in your server environment:
|
|
39
|
+
* 1. Install playwright package: `npm install playwright` or `pnpm add playwright`
|
|
40
|
+
* 2. Install browsers: `npx playwright install chromium`
|
|
41
|
+
*
|
|
42
|
+
* If running inside a Docker container (e.g., Alpine Linux), you must use an image that supports Playwright
|
|
43
|
+
* or manually install OS dependencies. Playwright provides official Docker images: `mcr.microsoft.com/playwright:v1.x.x-focal`.
|
|
26
44
|
*/
|
|
27
45
|
export class SSRSimpleWebComponentFilter implements Filter {
|
|
28
46
|
private welcomUrl = 'http://localhost';
|
|
47
|
+
private browser?: any;
|
|
29
48
|
|
|
30
49
|
constructor(public config: SWCSSRConfig) {
|
|
31
50
|
this.welcomUrl = config.welcomUrl || this.welcomUrl;
|
|
32
51
|
}
|
|
33
52
|
|
|
34
|
-
async onInit(app: SimpleBootHttpServer) {
|
|
53
|
+
async onInit(app: SimpleBootHttpServer) {
|
|
54
|
+
// 1. Initialize Playwright browser when the server starts
|
|
55
|
+
const { chromium } = await import('playwright');
|
|
56
|
+
this.browser = await chromium.launch({
|
|
57
|
+
headless: true,
|
|
58
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-web-security'] // Allow CORS for local dev
|
|
59
|
+
});
|
|
60
|
+
console.log('🚀 Playwright Browser initialized for SSR Filter');
|
|
61
|
+
}
|
|
35
62
|
|
|
36
|
-
async onDestroy() {
|
|
63
|
+
async onDestroy() {
|
|
64
|
+
if (this.browser) {
|
|
65
|
+
await this.browser.close();
|
|
66
|
+
console.log('🛑 Playwright Browser closed');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
37
69
|
|
|
38
|
-
async proceedBefore({ rr }: { rr: RequestResponse; app: SimpleBootHttpServer; carrier: Map<string, any> }) {
|
|
70
|
+
async proceedBefore({ rr, app, carrier }: { rr: RequestResponse; app: SimpleBootHttpServer; carrier: Map<string, any> }) {
|
|
39
71
|
if (this.config.ssrExcludeFilter?.(rr)) {
|
|
40
|
-
return
|
|
72
|
+
return true; // Bypass SSR and continue server chain
|
|
41
73
|
}
|
|
42
74
|
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
console.log('playwright2222222', rr.reqHeader('x-ssr-browser'));
|
|
76
|
+
|
|
77
|
+
// 2. Prevent Infinite Loop:
|
|
78
|
+
// If the request comes from Playwright itself, bypass SSR and serve the original static SPA file.
|
|
79
|
+
if (rr.reqHeader('x-ssr-browser') === 'playwright') {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (rr.reqIsMethod(HttpMethod.GET) && rr.reqHasContentTypeHeader(Mimes.TextHtml)) {
|
|
84
|
+
// Build target URL for Playwright to visit
|
|
85
|
+
// Use the server's actual listening address and port if available
|
|
86
|
+
const protocol = app.option.protocol || 'http';
|
|
87
|
+
const host = app.option.hostname || '127.0.0.1';
|
|
88
|
+
const port = app.option.port || 80;
|
|
89
|
+
|
|
90
|
+
const url = rr.reqUrlObj({ host: `${host}:${port}` });
|
|
91
|
+
url.protocol = protocol;
|
|
92
|
+
|
|
45
93
|
const targetUrl = url.toString() ?? this.welcomUrl;
|
|
46
94
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// web component 경우 자기 tagName을 생성자 에게 HTMLElementBase에넘겨줘야되기떄문에
|
|
52
|
-
const getTagName = (type: ConstructorType<any>) => {
|
|
53
|
-
const zz = getElementConfig(type);
|
|
54
|
-
return zz.name;
|
|
55
|
-
};
|
|
56
|
-
(window as any).HTMLElement = class extends HTMLElementBase {
|
|
57
|
-
constructor(...args: any[]) {
|
|
58
|
-
const ctor = new.target as any; // 이런 슈가 기능이...
|
|
59
|
-
const resolvedTagName = getTagName(ctor);
|
|
60
|
-
super(resolvedTagName, args[0]);
|
|
61
|
-
}
|
|
95
|
+
if (!this.browser) {
|
|
96
|
+
console.error('Playwright browser is not initialized. Bypassing SSR.');
|
|
97
|
+
return true;
|
|
62
98
|
}
|
|
63
99
|
|
|
100
|
+
// Create a fresh context for isolation
|
|
101
|
+
// IMPORTANT: We do NOT use `extraHTTPHeaders` globally because it attaches the header to EVERY request
|
|
102
|
+
// Playwright makes (including CORS requests to FontAwesome or API requests).
|
|
103
|
+
// Instead, we will set the header ONLY for the initial navigation request to our server.
|
|
104
|
+
const context = await this.browser.newContext();
|
|
105
|
+
const page = await context.newPage();
|
|
64
106
|
|
|
65
107
|
try {
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
108
|
+
// Intercept ONLY requests going to our local server and append the bypass header
|
|
109
|
+
await page.route('**/*', route => {
|
|
110
|
+
const request = route.request();
|
|
111
|
+
|
|
112
|
+
// Only append the x-ssr-browser header for requests to the local server that are NOT API requests
|
|
113
|
+
const isLocal = request.url().startsWith(targetUrl) || request.url().includes('localhost') || request.url().includes('127.0.0.1');
|
|
114
|
+
const isApi = request.url().includes('/api/');
|
|
115
|
+
|
|
116
|
+
if (isLocal && !isApi) {
|
|
117
|
+
const headers = {
|
|
118
|
+
...request.headers(),
|
|
119
|
+
'x-ssr-browser': 'playwright'
|
|
120
|
+
};
|
|
121
|
+
route.continue({ headers }).catch(() => {});
|
|
122
|
+
} else {
|
|
123
|
+
// For external requests (like font-awesome CORS) or API calls, do not add the custom header
|
|
124
|
+
route.continue().catch(() => {});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// 3. Navigate to the page and wait for the framework to finish rendering
|
|
129
|
+
// but normally Playwright just navigates to the targetUrl and loads the real SPA resources.
|
|
130
|
+
|
|
131
|
+
// 💡 Playwright ↔ Framework 통신 설정
|
|
132
|
+
// 프론트엔드의 `bootfactory.ts` 안의 `onRouteChanged` 등에서
|
|
133
|
+
// `if (window.onPlaywrightReady) window.onPlaywrightReady()` 라고 호출해주면,
|
|
134
|
+
// 아래의 Promise가 즉시 resolve 되도록 연결합니다.
|
|
135
|
+
let resolvePlaywrightReady: () => void;
|
|
136
|
+
const playwrightReadyPromise = new Promise<void>(resolve => {
|
|
137
|
+
resolvePlaywrightReady = resolve;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await page.exposeFunction('onPlaywrightReady', () => {
|
|
141
|
+
console.log('⚡️ [SSR:Playwright] Received "onPlaywrightReady" signal from framework!');
|
|
142
|
+
resolvePlaywrightReady();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// 3. Navigate to the page and wait for the framework to finish rendering
|
|
146
|
+
// Since the server will return the `index.html` (due to the bypass header), Playwright will download
|
|
147
|
+
// your main.js / bundle.js, evaluate it, and the Custom Elements will register themselves naturally!
|
|
148
|
+
await page.goto(targetUrl, {
|
|
149
|
+
waitUntil: this.config.playwright?.waitUntil ?? 'networkidle',
|
|
150
|
+
timeout: this.config.playwright?.timeout ?? 15000
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// 💡 Framework Load Wait (Crucial for SWC)
|
|
154
|
+
// Wait for EITHER the framework to call `window.onPlaywrightReady()` OR a specific innerHTML condition
|
|
155
|
+
await Promise.race([
|
|
156
|
+
playwrightReadyPromise, // 직접 exposeFunction에서 트리거된 Promise
|
|
157
|
+
|
|
158
|
+
page.waitForFunction(
|
|
159
|
+
() => {
|
|
160
|
+
// Fallback 1: If explicit signal variable is set
|
|
161
|
+
return (window as any)._playwrightReadySignalFired === true;
|
|
162
|
+
},
|
|
163
|
+
{ timeout: this.config.playwright?.timeout ?? 15000 }
|
|
164
|
+
),
|
|
165
|
+
|
|
166
|
+
page.waitForFunction(
|
|
167
|
+
() => {
|
|
168
|
+
// Fallback 2: If no signal, just wait until body has content
|
|
169
|
+
return document.body.innerHTML.trim().length > 100;
|
|
170
|
+
},
|
|
171
|
+
{ timeout: this.config.playwright?.timeout ?? 15000 }
|
|
172
|
+
)
|
|
173
|
+
]).catch(() => console.log('Wait for app mount timeout... proceeding anyway'));
|
|
174
|
+
|
|
175
|
+
// 강제로 컴포넌트 렌더링 및 폰트어썸 등 리소스 로딩 대기 시간 추가 (안전 장치)
|
|
176
|
+
// onPlaywrightReady 신호가 떨어져도, 비동기 렌더링(SwcLoop 등)이 DOM에 완전히 반영되는 데
|
|
177
|
+
// 약간의 Microtask 지연이 있을 수 있으므로 아주 짧게만 기다려 줍니다.
|
|
178
|
+
// await new Promise(resolve => setTimeout(resolve, 300));
|
|
179
|
+
|
|
180
|
+
// Optional: Wait for a specific component to be visible
|
|
181
|
+
if (this.config.playwright?.waitForSelector) {
|
|
182
|
+
await page.waitForSelector(this.config.playwright.waitForSelector, {
|
|
183
|
+
timeout: this.config.playwright?.timeout ?? 15000
|
|
184
|
+
});
|
|
70
185
|
}
|
|
71
186
|
|
|
72
|
-
//
|
|
73
|
-
|
|
187
|
+
// Optional: Hardcoded wait time if explicitly set (fallback)
|
|
188
|
+
if (this.config.playwright?.waitForTimeout) {
|
|
189
|
+
await page.waitForTimeout(this.config.playwright.waitForTimeout);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 4. Set SSR Attribute before extracting HTML
|
|
193
|
+
await page.evaluate(() => {
|
|
194
|
+
document.body.setAttribute('ssr-use', 'true');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// 5. Generate Final HTML
|
|
198
|
+
const html = await page.evaluate(ignoreTags => {
|
|
199
|
+
const skipTags = new Set(ignoreTags || []);
|
|
200
|
+
|
|
201
|
+
function serializeNode(node: Node, inBody: boolean = false): string {
|
|
202
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
203
|
+
const text = node.textContent || '';
|
|
204
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
205
|
+
}
|
|
206
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
207
|
+
return `<!--${node.textContent}-->`;
|
|
208
|
+
}
|
|
209
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
210
|
+
return '';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const el = node as Element;
|
|
214
|
+
const tag = el.tagName.toLowerCase();
|
|
215
|
+
const isBody = tag === 'body';
|
|
216
|
+
const isInsideBody = inBody || isBody;
|
|
217
|
+
|
|
218
|
+
// 🚫 Skip ignored tags (like <style>) ONLY if we are inside the <body>.
|
|
219
|
+
// This ensures <style> tags in the <head> (global styles) are preserved!
|
|
220
|
+
if (isInsideBody && skipTags.has(tag)) {
|
|
221
|
+
return '';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let s = `<${tag}`;
|
|
225
|
+
|
|
226
|
+
// 1. Serialize Attributes
|
|
227
|
+
for (let i = 0; i < el.attributes.length; i++) {
|
|
228
|
+
const attr = el.attributes[i];
|
|
229
|
+
s += ` ${attr.name}="${attr.value.replace(/"/g, '"')}"`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Handle void elements (self-closing)
|
|
233
|
+
const voidElements = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
|
|
234
|
+
if (voidElements.has(tag)) {
|
|
235
|
+
s += '>';
|
|
236
|
+
return s;
|
|
237
|
+
}
|
|
238
|
+
s += '>';
|
|
239
|
+
|
|
240
|
+
// 2. Serialize Shadow DOM (Declarative Shadow DOM)
|
|
241
|
+
// MUST KEEP for SSR: Without this, the user receives an empty shell until JS loads and re-renders everything.
|
|
242
|
+
// This allows the browser to paint the full UI (including styles inside the component) immediately.
|
|
243
|
+
if (el.shadowRoot) {
|
|
244
|
+
s += `<template shadowrootmode="${el.shadowRoot.mode}">`;
|
|
245
|
+
for (let i = 0; i < el.shadowRoot.childNodes.length; i++) {
|
|
246
|
+
s += serializeNode(el.shadowRoot.childNodes[i], isInsideBody);
|
|
247
|
+
}
|
|
248
|
+
s += '</template>';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// 3. Serialize Light DOM Children
|
|
252
|
+
for (let i = 0; i < el.childNodes.length; i++) {
|
|
253
|
+
s += serializeNode(el.childNodes[i], isInsideBody);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
s += `</${tag}>`;
|
|
257
|
+
return s;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let result = '<!DOCTYPE html>\n<html';
|
|
261
|
+
const htmlNode = document.documentElement;
|
|
262
|
+
for (let i = 0; i < htmlNode.attributes.length; i++) {
|
|
263
|
+
const attr = htmlNode.attributes[i];
|
|
264
|
+
result += ` ${attr.name}="${attr.value.replace(/"/g, '"')}"`;
|
|
265
|
+
}
|
|
266
|
+
result += '>\n';
|
|
267
|
+
|
|
268
|
+
if (document.head) result += serializeNode(document.head, false);
|
|
269
|
+
result += '\n';
|
|
270
|
+
if (document.body) result += serializeNode(document.body, true);
|
|
271
|
+
|
|
272
|
+
result += '\n</html>';
|
|
273
|
+
return result;
|
|
274
|
+
}, this.config.playwright?.ignoreTags || []);
|
|
275
|
+
console.log(`💈 [SSR:Playwright] Successfully rendered ${targetUrl}`);
|
|
74
276
|
|
|
75
|
-
await this.writeOkHtmlAndEnd({ rr }, html);
|
|
277
|
+
await this.writeOkHtmlAndEnd({ rr }, this.makeHTML(html));
|
|
278
|
+
} catch (e) {
|
|
279
|
+
console.error(`Playwright SSR Error for ${targetUrl}:`, e);
|
|
280
|
+
// If Playwright fails (e.g., timeout), we bypass SSR so the client at least gets the CSR app
|
|
281
|
+
return true;
|
|
76
282
|
} finally {
|
|
77
|
-
|
|
78
|
-
(window as any).close?.();
|
|
79
|
-
domParserInitializer.destroy();
|
|
283
|
+
await context.close();
|
|
80
284
|
}
|
|
81
|
-
return false;
|
|
285
|
+
return false; // Tell SimpleBootHttpServer to stop routing because we've handled the response
|
|
82
286
|
}
|
|
83
287
|
return true;
|
|
84
288
|
}
|
|
85
289
|
|
|
86
|
-
makeHTML(
|
|
87
|
-
let html = window.document.documentElement.outerHTML;
|
|
290
|
+
makeHTML(html: string) {
|
|
88
291
|
if (!/^<!DOCTYPE html>/i.test(html)) {
|
|
89
292
|
html = '<!DOCTYPE html>\n' + html;
|
|
90
293
|
}
|
package/src/filters/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export * from '@dooboostore/simple-boot-http-server/filters/IntentSchemeFilter';
|
|
2
1
|
export * from './SSRFilter';
|
|
3
|
-
export * from './SSRWorker';
|
|
2
|
+
export * from './SSRWorker';
|
|
3
|
+
export * from './SSRSimpleWebComponentFilter';
|
|
4
|
+
export * from './SSRSimpleWebComponentDomParserFilter';
|
|
5
|
+
export * from './SSRDomParserFilter';
|
|
6
|
+
export * from './SSRLinkDomFilter';
|
package/src/index.ts
CHANGED
package/README.MD
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# @dooboostore/simple-boot-http-server-ssr
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@dooboostore/simple-boot-http-server-ssr)
|
|
4
|
-
[](https://github.com/dooboostore-develop/packages/actions/workflows/main.yaml)
|
|
5
|
-
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
|
|
7
|
-
**Full Documentation:** [https://dooboostore-develop.github.io/@dooboostore/simple-boot-http-server-ssr](https://dooboostore-develop.github.io/@dooboostore/simple-boot-http-server-ssr)
|
|
8
|
-
|
|
9
|
-
A framework for building Server-Side Rendered (SSR) applications by seamlessly integrating `@dooboostore/simple-boot-front` and `@dooboostore/simple-boot-http-server`.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Features
|
|
14
|
-
|
|
15
|
-
- **Server-Side Rendering**: Renders your single-page application on the server for fast, SEO-friendly websites.
|
|
16
|
-
- **JSDOM Integration**: Uses `jsdom` to create a virtual DOM environment on the server, allowing front-end code to run.
|
|
17
|
-
- **Component Pooling**: Manages a pool of `SimpleBootFront` instances to handle concurrent SSR requests efficiently.
|
|
18
|
-
- **Automatic Data Hydration**: Serializes data fetched on the server and hydrates it on the client to avoid redundant API calls.
|
|
19
|
-
- **Unified Codebase**: Enables sharing of routing, services, and components between the server and the client.
|
|
20
|
-
- **Intent-based Communication**: Provides a mechanism for client-server communication using custom HTTP headers.
|
|
21
|
-
- **SimpleWebComponent Integration**: Seamless SSR support for `@dooboostore/simple-web-component` with automatic component registration and hydration.
|
|
22
|
-
|
|
23
|
-
## Quick Start
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npx @dooboostore/create-simple-boot-http-server-ssr my-ssr-app
|
|
27
|
-
cd my-ssr-app
|
|
28
|
-
npm start
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## SimpleWebComponent SSR Integration
|
|
34
|
-
|
|
35
|
-
This package enables seamless Server-Side Rendering (SSR) for `@dooboostore/simple-web-component` applications. Here's how it works:
|
|
36
|
-
|
|
37
|
-
### Backend Setup
|
|
38
|
-
|
|
39
|
-
Configure the server with `SSRSimpleWebComponentFilter` to handle SWC component registration and rendering:
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
import { SimpleBootHttpSSRServer } from '@dooboostore/simple-boot-http-server-ssr/SimpleBootHttpSSRServer';
|
|
43
|
-
import { HttpSSRServerOption } from '@dooboostore/simple-boot-http-server-ssr/option';
|
|
44
|
-
import { SSRSimpleWebComponentFilter } from '@dooboostore/simple-boot-http-server-ssr/filters/SSRSimpleWebComponentFilter';
|
|
45
|
-
import { ResourceFilter } from '@dooboostore/simple-boot-http-server/filters';
|
|
46
|
-
import swcRegister from '@dooboostore/simple-web-component';
|
|
47
|
-
import bootfactory from './bootfactory';
|
|
48
|
-
|
|
49
|
-
const swcFilter = new SSRSimpleWebComponentFilter({
|
|
50
|
-
frontDistPath: './dist-front-end',
|
|
51
|
-
frontDistIndexFileName: 'index.html',
|
|
52
|
-
registerComponents: async (window: any) => {
|
|
53
|
-
// Register SWC components in the virtual DOM
|
|
54
|
-
swcRegister(window);
|
|
55
|
-
const urlPath = window.location.pathname;
|
|
56
|
-
// Initialize your app with bootfactory
|
|
57
|
-
bootfactory(window, urlPath);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const server = new SimpleBootHttpSSRServer(
|
|
62
|
-
new HttpSSRServerOption({
|
|
63
|
-
listen: { port: 8080 },
|
|
64
|
-
filters: [new ResourceFilter('./dist-front-end'), swcFilter]
|
|
65
|
-
})
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
server.run();
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Universal Bootfactory Pattern
|
|
72
|
-
|
|
73
|
-
Use a universal bootfactory that runs on both server and client, detecting the environment and choosing the appropriate connection mode:
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
// src/bootfactory.ts
|
|
77
|
-
import register from '@dooboostore/simple-web-component';
|
|
78
|
-
import { componentFactories } from './components';
|
|
79
|
-
import { pageFactories } from './pages';
|
|
80
|
-
|
|
81
|
-
export default (window: Window, urlPath?: string) => {
|
|
82
|
-
// Register all SWC components
|
|
83
|
-
register(window, [...componentFactories, ...pageFactories]);
|
|
84
|
-
|
|
85
|
-
const appElement = window.document.querySelector('#app');
|
|
86
|
-
if (appElement?.connect) {
|
|
87
|
-
const isClientWindow = typeof window !== 'undefined' && window === globalThis.window;
|
|
88
|
-
|
|
89
|
-
appElement.connect({
|
|
90
|
-
rootRouter: RootRouter.SYMBOL,
|
|
91
|
-
routeType: 'path',
|
|
92
|
-
path: urlPath ?? '/',
|
|
93
|
-
window,
|
|
94
|
-
// Server renders with direct mode, client hydrates with swap mode
|
|
95
|
-
connectMode: isClientWindow ? 'swap' : 'direct'
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return appElement;
|
|
100
|
-
};
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Client Entry Point
|
|
104
|
-
|
|
105
|
-
The client-side entry point simply calls the same bootfactory to hydrate the server-rendered markup:
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
// front-end/index.ts
|
|
109
|
-
import bootfactory from './bootfactory';
|
|
110
|
-
import { UrlUtils } from '@dooboostore/core/url';
|
|
111
|
-
|
|
112
|
-
console.log('🚀 Hydrating with SwcApplication...');
|
|
113
|
-
const w = window;
|
|
114
|
-
bootfactory(w, UrlUtils.getUrlPath(w.location));
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### How It Works
|
|
118
|
-
|
|
119
|
-
1. **Server Request**: Client sends GET request to `/user/123`
|
|
120
|
-
2. **Virtual DOM Rendering**: `SSRSimpleWebComponentFilter` creates a virtual DOM environment via JSDOM
|
|
121
|
-
3. **Component Registration**: Your `registerComponents` callback registers all SWC components
|
|
122
|
-
4. **Bootfactory Initialization**: Bootfactory connects the app to the root router in **direct mode**
|
|
123
|
-
5. **HTML Serialization**: The rendered virtual DOM is serialized to HTML and sent to the client
|
|
124
|
-
6. **Client Hydration**: Browser loads the JavaScript and calls bootfactory again in **swap mode**
|
|
125
|
-
7. **Zero FOUC**: User sees the fully rendered page immediately; JavaScript takes over seamlessly
|
|
126
|
-
|
|
127
|
-
### Key Benefits
|
|
128
|
-
|
|
129
|
-
| Benefit | Description |
|
|
130
|
-
|---------|-------------|
|
|
131
|
-
| **SEO-Friendly** | Full HTML sent to search engines; all content indexable |
|
|
132
|
-
| **Fast FCP** | No "loading..." spinner; page is immediately interactive |
|
|
133
|
-
| **Preserved Tags** | Server-rendered DOM tags (data attributes, ARIA labels) remain intact |
|
|
134
|
-
| **Code Sharing** | Same routing, services, and components on server and client |
|
|
135
|
-
| **Zero FOUC** | Flash of Unstyled Content eliminated; page renders on server first |
|
|
136
|
-
| **Progressive Enhancement** | Core content works without JavaScript |
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Learn More
|
|
141
|
-
|
|
142
|
-
The detailed API documentation is available on our documentation website.
|
|
143
|
-
|
|
144
|
-
## Security Hardening
|
|
145
|
-
|
|
146
|
-
Use the following baseline controls before deploying SSR applications to production:
|
|
147
|
-
|
|
148
|
-
- Restrict trusted origins and set strict CORS policy.
|
|
149
|
-
- Sanitize any user-derived values injected into HTML templates.
|
|
150
|
-
- Set `Content-Security-Policy`, `X-Content-Type-Options`, and `X-Frame-Options` headers.
|
|
151
|
-
- Validate and bound request payload size, query parameters, and header length.
|
|
152
|
-
- Use secure cookies (`HttpOnly`, `Secure`, `SameSite`) for session-related data.
|
|
153
|
-
- Add SSR timeout and concurrency limits to prevent resource exhaustion.
|
|
154
|
-
- Keep dependency versions updated and run vulnerability scans in CI.
|
|
155
|
-
|
|
156
|
-
### Minimal Production Checklist
|
|
157
|
-
|
|
158
|
-
- HTTPS enforced end-to-end.
|
|
159
|
-
- Origin/cookie policy reviewed.
|
|
160
|
-
- CSP configured and tested for hydration scripts.
|
|
161
|
-
- Error responses avoid leaking stack traces.
|
|
162
|
-
- Request logs redact secrets and personal data.
|
|
163
|
-
- SSR pool size and queue limits tuned for expected traffic.
|
|
164
|
-
|
|
165
|
-
## License
|
|
166
|
-
|
|
167
|
-
This package is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __export = (target, all) => {
|
|
6
|
-
for (var name in all)
|
|
7
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
-
};
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
-
var LoadAroundBefore_exports = {};
|
|
19
|
-
__export(LoadAroundBefore_exports, {
|
|
20
|
-
LoadAroundBefore: () => LoadAroundBefore
|
|
21
|
-
});
|
|
22
|
-
module.exports = __toCommonJS(LoadAroundBefore_exports);
|
|
23
|
-
var import_SimDecorator = require("@dooboostore/simple-boot/decorators/SimDecorator");
|
|
24
|
-
var import_AOPDecorator = require("@dooboostore/simple-boot/decorators/aop/AOPDecorator");
|
|
25
|
-
var import_core = require("@dooboostore/core");
|
|
26
|
-
const LoadAroundBefore = (obj, propertyKey, args) => {
|
|
27
|
-
const simstanceManager = obj._SimpleBoot_simstanceManager;
|
|
28
|
-
const simOption = obj._SimpleBoot_simOption;
|
|
29
|
-
const config = (0, import_SimDecorator.getSim)(obj);
|
|
30
|
-
if (simstanceManager && simOption && config?.scheme) {
|
|
31
|
-
const key = config.scheme + "_" + propertyKey;
|
|
32
|
-
const type = import_core.ReflectUtils.getReturnType(obj, propertyKey);
|
|
33
|
-
const isHas = key in (simOption.window.server_side_data ?? {});
|
|
34
|
-
if (isHas) {
|
|
35
|
-
const data = simOption.window.server_side_data?.[key];
|
|
36
|
-
delete simOption.window.server_side_data?.[key];
|
|
37
|
-
let rdata;
|
|
38
|
-
if (type === Promise) {
|
|
39
|
-
rdata = Promise.resolve(data);
|
|
40
|
-
} else {
|
|
41
|
-
rdata = data;
|
|
42
|
-
}
|
|
43
|
-
throw new import_AOPDecorator.AroundForceReturn(rdata);
|
|
44
|
-
} else {
|
|
45
|
-
return args;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return args;
|
|
49
|
-
};
|
|
50
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
-
0 && (module.exports = {
|
|
52
|
-
LoadAroundBefore
|
|
53
|
-
});
|
|
54
|
-
//# sourceMappingURL=LoadAroundBefore.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/decorators/LoadAroundBefore.ts"],
|
|
4
|
-
"sourcesContent": ["import { getSim } from '@dooboostore/simple-boot/decorators/SimDecorator';\nimport { AroundForceReturn } from '@dooboostore/simple-boot/decorators/aop/AOPDecorator';\nimport { ReflectUtils } from '@dooboostore/core';\n\nexport const LoadAroundBefore = (obj: any, propertyKey: string, args: any[]) => {\n const simstanceManager = obj._SimpleBoot_simstanceManager;\n const simOption = obj._SimpleBoot_simOption;\n const config = getSim(obj);\n if (simstanceManager && simOption && config?.scheme) {\n const key = config.scheme + '_' + propertyKey;\n const type = ReflectUtils.getReturnType(obj, propertyKey);\n const isHas = (key in (simOption.window.server_side_data ?? {}))\n if (isHas) {\n const data = simOption.window.server_side_data?.[key];\n delete simOption.window.server_side_data?.[key];\n let rdata;\n if (type === Promise) {\n rdata = Promise.resolve(data);\n } else {\n rdata = data;\n }\n throw new AroundForceReturn(rdata);\n } else {\n return args;\n }\n }\n return args;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAuB;AACvB,0BAAkC;AAClC,kBAA6B;AAEtB,MAAM,mBAAmB,CAAC,KAAU,aAAqB,SAAgB;AAC5E,QAAM,mBAAmB,IAAI;AAC7B,QAAM,YAAY,IAAI;AACtB,QAAM,aAAS,4BAAO,GAAG;AACzB,MAAI,oBAAoB,aAAa,QAAQ,QAAQ;AACjD,UAAM,MAAM,OAAO,SAAS,MAAM;AAClC,UAAM,OAAO,yBAAa,cAAc,KAAK,WAAW;AACxD,UAAM,QAAS,QAAQ,UAAU,OAAO,oBAAoB,CAAC;AAC7D,QAAI,OAAO;AACP,YAAM,OAAO,UAAU,OAAO,mBAAmB,GAAG;AACpD,aAAO,UAAU,OAAO,mBAAmB,GAAG;AAC9C,UAAI;AACJ,UAAI,SAAS,SAAS;AAClB,gBAAQ,QAAQ,QAAQ,IAAI;AAAA,MAChC,OAAO;AACH,gBAAQ;AAAA,MACZ;AACA,YAAM,IAAI,sCAAkB,KAAK;AAAA,IACrC,OAAO;AACH,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __export = (target, all) => {
|
|
6
|
-
for (var name in all)
|
|
7
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
-
};
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
-
var SaveAroundAfter_exports = {};
|
|
19
|
-
__export(SaveAroundAfter_exports, {
|
|
20
|
-
SaveAroundAfter: () => SaveAroundAfter,
|
|
21
|
-
getStorage: () => getStorage
|
|
22
|
-
});
|
|
23
|
-
module.exports = __toCommonJS(SaveAroundAfter_exports);
|
|
24
|
-
var import_SimDecorator = require("@dooboostore/simple-boot/decorators/SimDecorator");
|
|
25
|
-
const SaveAroundAfter = (obj, propertyKey, args, beforeReturn) => {
|
|
26
|
-
const simstanceManager = obj._SimpleBoot_simstanceManager;
|
|
27
|
-
const simOption = obj._SimpleBoot_simOption;
|
|
28
|
-
const config = (0, import_SimDecorator.getSim)(obj);
|
|
29
|
-
if (simstanceManager && simOption && config?.scheme) {
|
|
30
|
-
if (!simOption.window.server_side_data) {
|
|
31
|
-
simOption.window.server_side_data = {};
|
|
32
|
-
}
|
|
33
|
-
const key = config.scheme + "_" + propertyKey;
|
|
34
|
-
if (beforeReturn instanceof Promise) {
|
|
35
|
-
beforeReturn.then((it) => {
|
|
36
|
-
simOption.window.server_side_data[key] = JSON.stringify(it);
|
|
37
|
-
return it;
|
|
38
|
-
});
|
|
39
|
-
} else {
|
|
40
|
-
simOption.window.server_side_data[key] = JSON.stringify(beforeReturn);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return beforeReturn;
|
|
44
|
-
};
|
|
45
|
-
const getStorage = (window) => {
|
|
46
|
-
return window.aroundStorage;
|
|
47
|
-
};
|
|
48
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
49
|
-
0 && (module.exports = {
|
|
50
|
-
SaveAroundAfter,
|
|
51
|
-
getStorage
|
|
52
|
-
});
|
|
53
|
-
//# sourceMappingURL=SaveAroundAfter.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/decorators/SaveAroundAfter.ts"],
|
|
4
|
-
"sourcesContent": ["import { getSim } from '@dooboostore/simple-boot/decorators/SimDecorator';\n\nexport const SaveAroundAfter = (obj: any, propertyKey: string, args: any[], beforeReturn: any) => {\n const simstanceManager = obj._SimpleBoot_simstanceManager;\n const simOption = obj._SimpleBoot_simOption;\n const config = getSim(obj);\n if (simstanceManager && simOption && config?.scheme) {\n if (!simOption.window.server_side_data) {\n simOption.window.server_side_data = {};\n }\n const key = config.scheme+'_'+propertyKey;\n if (beforeReturn instanceof Promise) {\n beforeReturn.then((it) => {\n simOption.window.server_side_data[key] = JSON.stringify(it);\n return it;\n });\n } else {\n simOption.window.server_side_data[key] = JSON.stringify(beforeReturn);\n }\n }\n return beforeReturn;\n}\n\n\nexport const getStorage = (window: Window) => {\n return (window as any).aroundStorage;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAuB;AAEhB,MAAM,kBAAkB,CAAC,KAAU,aAAqB,MAAa,iBAAsB;AAC9F,QAAM,mBAAmB,IAAI;AAC7B,QAAM,YAAY,IAAI;AACtB,QAAM,aAAS,4BAAO,GAAG;AACzB,MAAI,oBAAoB,aAAa,QAAQ,QAAQ;AACjD,QAAI,CAAC,UAAU,OAAO,kBAAkB;AACpC,gBAAU,OAAO,mBAAmB,CAAC;AAAA,IACzC;AACA,UAAM,MAAM,OAAO,SAAO,MAAI;AAC9B,QAAI,wBAAwB,SAAS;AACjC,mBAAa,KAAK,CAAC,OAAO;AACtB,kBAAU,OAAO,iBAAiB,GAAG,IAAI,KAAK,UAAU,EAAE;AAC1D,eAAO;AAAA,MACX,CAAC;AAAA,IACL,OAAO;AACH,gBAAU,OAAO,iBAAiB,GAAG,IAAI,KAAK,UAAU,YAAY;AAAA,IACxE;AAAA,EACJ;AACA,SAAO;AACX;AAGO,MAAM,aAAa,CAAC,WAAmB;AAC1C,SAAQ,OAAe;AAC3B;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __copyProps = (to, from, except, desc) => {
|
|
6
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
7
|
-
for (let key of __getOwnPropNames(from))
|
|
8
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
9
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
10
|
-
}
|
|
11
|
-
return to;
|
|
12
|
-
};
|
|
13
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
14
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
-
var decorators_exports = {};
|
|
16
|
-
module.exports = __toCommonJS(decorators_exports);
|
|
17
|
-
__reExport(decorators_exports, require("./LoadAroundBefore"), module.exports);
|
|
18
|
-
__reExport(decorators_exports, require("./SaveAroundAfter"), module.exports);
|
|
19
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
20
|
-
0 && (module.exports = {
|
|
21
|
-
...require("./LoadAroundBefore"),
|
|
22
|
-
...require("./SaveAroundAfter")
|
|
23
|
-
});
|
|
24
|
-
//# sourceMappingURL=index.js.map
|