@happy-dom/server-renderer 20.0.10 → 20.1.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.
Files changed (72) hide show
  1. package/lib/ServerRenderer.d.ts +3 -3
  2. package/lib/ServerRenderer.d.ts.map +1 -1
  3. package/lib/ServerRenderer.js +27 -13
  4. package/lib/ServerRenderer.js.map +1 -1
  5. package/lib/ServerRendererBrowser.d.ts.map +1 -1
  6. package/lib/ServerRendererBrowser.js +7 -133
  7. package/lib/ServerRendererBrowser.js.map +1 -1
  8. package/lib/{ServerRendererWorker.d.ts → ServerRendererBrowserWorker.d.ts} +2 -2
  9. package/lib/ServerRendererBrowserWorker.d.ts.map +1 -0
  10. package/lib/{ServerRendererWorker.js → ServerRendererBrowserWorker.js} +3 -3
  11. package/lib/ServerRendererBrowserWorker.js.map +1 -0
  12. package/lib/ServerRendererPage.d.ts +24 -0
  13. package/lib/ServerRendererPage.d.ts.map +1 -0
  14. package/lib/ServerRendererPage.js +234 -0
  15. package/lib/ServerRendererPage.js.map +1 -0
  16. package/lib/ServerRendererPageWorker.d.ts +15 -0
  17. package/lib/ServerRendererPageWorker.d.ts.map +1 -0
  18. package/lib/ServerRendererPageWorker.js +38 -0
  19. package/lib/ServerRendererPageWorker.js.map +1 -0
  20. package/lib/ServerRendererServer.d.ts.map +1 -1
  21. package/lib/ServerRendererServer.js +0 -4
  22. package/lib/ServerRendererServer.js.map +1 -1
  23. package/lib/config/DefaultServerRendererConfiguration.d.ts.map +1 -1
  24. package/lib/config/DefaultServerRendererConfiguration.js +6 -6
  25. package/lib/config/DefaultServerRendererConfiguration.js.map +1 -1
  26. package/lib/enums/ServerRendererModeEnum.d.ts +6 -0
  27. package/lib/enums/ServerRendererModeEnum.d.ts.map +1 -0
  28. package/lib/enums/ServerRendererModeEnum.js +9 -0
  29. package/lib/enums/ServerRendererModeEnum.js.map +1 -0
  30. package/lib/index.d.ts +2 -1
  31. package/lib/index.d.ts.map +1 -1
  32. package/lib/index.js +2 -1
  33. package/lib/index.js.map +1 -1
  34. package/lib/types/IOptionalServerRendererConfiguration.d.ts +11 -2
  35. package/lib/types/IOptionalServerRendererConfiguration.d.ts.map +1 -1
  36. package/lib/types/IServerRendererConfiguration.d.ts +11 -2
  37. package/lib/types/IServerRendererConfiguration.d.ts.map +1 -1
  38. package/lib/types/IServerRendererItem.d.ts +2 -1
  39. package/lib/types/IServerRendererItem.d.ts.map +1 -1
  40. package/lib/types/IServerRendererResult.d.ts +4 -4
  41. package/lib/types/IServerRendererResult.d.ts.map +1 -1
  42. package/lib/utilities/BrowserWindowPolyfill.d.ts.map +1 -1
  43. package/lib/utilities/BrowserWindowPolyfill.js +2 -2
  44. package/lib/utilities/BrowserWindowPolyfill.js.map +1 -1
  45. package/lib/utilities/HelpPrinterRows.d.ts.map +1 -1
  46. package/lib/utilities/HelpPrinterRows.js +25 -4
  47. package/lib/utilities/HelpPrinterRows.js.map +1 -1
  48. package/lib/utilities/ProcessArgumentsParser.d.ts.map +1 -1
  49. package/lib/utilities/ProcessArgumentsParser.js +86 -22
  50. package/lib/utilities/ProcessArgumentsParser.js.map +1 -1
  51. package/lib/utilities/ServerRendererConfigurationFactory.js +1 -1
  52. package/lib/utilities/ServerRendererConfigurationFactory.js.map +1 -1
  53. package/package.json +3 -2
  54. package/src/ServerRenderer.ts +36 -13
  55. package/src/ServerRendererBrowser.ts +7 -151
  56. package/src/{ServerRendererWorker.ts → ServerRendererBrowserWorker.ts} +2 -2
  57. package/src/ServerRendererPage.ts +279 -0
  58. package/src/ServerRendererPageWorker.ts +55 -0
  59. package/src/ServerRendererServer.ts +3 -8
  60. package/src/config/DefaultServerRendererConfiguration.ts +6 -6
  61. package/src/enums/ServerRendererModeEnum.ts +8 -0
  62. package/src/index.ts +3 -1
  63. package/src/types/IOptionalServerRendererConfiguration.ts +21 -2
  64. package/src/types/IServerRendererConfiguration.ts +20 -2
  65. package/src/types/IServerRendererItem.ts +2 -1
  66. package/src/types/IServerRendererResult.ts +4 -4
  67. package/src/utilities/BrowserWindowPolyfill.ts +3 -2
  68. package/src/utilities/HelpPrinterRows.ts +25 -4
  69. package/src/utilities/ProcessArgumentsParser.ts +93 -24
  70. package/src/utilities/ServerRendererConfigurationFactory.ts +1 -1
  71. package/lib/ServerRendererWorker.d.ts.map +0 -1
  72. package/lib/ServerRendererWorker.js.map +0 -1
@@ -1,32 +1,39 @@
1
1
  import type IOptionalBrowserSettings from 'happy-dom/lib/browser/types/IOptionalBrowserSettings.js';
2
2
  import ServerRendererLogLevelEnum from '../enums/ServerRendererLogLevelEnum.js';
3
3
  import IServerRendererItem from './IServerRendererItem.js';
4
+ import ServerRendererModeEnum from '../enums/ServerRendererModeEnum.js';
4
5
 
5
6
  export default interface IOptionalServerRendererConfiguration {
6
7
  /**
7
8
  * Settings for the browser.
8
9
  */
9
10
  browser?: IOptionalBrowserSettings;
11
+
10
12
  /**
11
13
  * Log level for the server renderer.
12
14
  */
13
15
  logLevel?: ServerRendererLogLevelEnum;
16
+
14
17
  /**
15
18
  * Enables debugging. This will override "browser.debug.traceWaitUntilComplete".
16
19
  */
17
20
  debug?: boolean;
21
+
18
22
  /**
19
23
  * Enables inspector.
20
24
  */
21
25
  inspect?: boolean;
26
+
22
27
  /**
23
28
  * Shows help information and exits.
24
29
  */
25
30
  help?: boolean;
31
+
26
32
  /**
27
33
  * Output directory.
28
34
  */
29
35
  outputDirectory?: string;
36
+
30
37
  /**
31
38
  * Cache settings.
32
39
  */
@@ -44,6 +51,7 @@ export default interface IOptionalServerRendererConfiguration {
44
51
  */
45
52
  warmup?: boolean;
46
53
  };
54
+
47
55
  /**
48
56
  * Settings for the worker.
49
57
  */
@@ -57,6 +65,7 @@ export default interface IOptionalServerRendererConfiguration {
57
65
  */
58
66
  maxConcurrency?: number;
59
67
  };
68
+
60
69
  /**
61
70
  * Settings for rendering.
62
71
  */
@@ -89,11 +98,21 @@ export default interface IOptionalServerRendererConfiguration {
89
98
  * Disable polyfills used for unimplemented functionality.
90
99
  */
91
100
  disablePolyfills?: boolean;
101
+ /**
102
+ * Setup script to be injected before rendering.
103
+ */
104
+ setupScript?: string | null;
105
+ /**
106
+ * Rendering mode.
107
+ */
108
+ mode?: ServerRendererModeEnum;
92
109
  };
110
+
93
111
  /**
94
- * List of URLs to render.
112
+ * List render items. Each item can be a URL string or an object specifying the URL or HTML string along with additional options.
95
113
  */
96
- urls?: Array<string | IServerRendererItem> | null;
114
+ renderItems?: Array<string | IServerRendererItem> | null;
115
+
97
116
  /**
98
117
  * Proxy server settings.
99
118
  */
@@ -1,24 +1,29 @@
1
1
  import type IBrowserSettings from 'happy-dom/lib/browser/types/IBrowserSettings.js';
2
2
  import ServerRendererLogLevelEnum from '../enums/ServerRendererLogLevelEnum.js';
3
3
  import IServerRendererItem from './IServerRendererItem.js';
4
+ import ServerRendererModeEnum from '../enums/ServerRendererModeEnum.js';
4
5
 
5
6
  export default interface IServerRendererConfiguration {
6
7
  /**
7
8
  * Settings for the browser.
8
9
  */
9
10
  browser: IBrowserSettings;
11
+
10
12
  /**
11
13
  * Log level for the server renderer.
12
14
  */
13
15
  logLevel: ServerRendererLogLevelEnum;
16
+
14
17
  /**
15
18
  * Enables debugging. This will override "browser.debug.traceWaitUntilComplete".
16
19
  */
17
20
  debug: boolean;
21
+
18
22
  /**
19
23
  * Enables inspector.
20
24
  */
21
25
  inspect: boolean;
26
+
22
27
  /**
23
28
  * Shows help information and exits.
24
29
  */
@@ -27,6 +32,7 @@ export default interface IServerRendererConfiguration {
27
32
  * Output directory.
28
33
  */
29
34
  outputDirectory: string;
35
+
30
36
  /**
31
37
  * Cache settings.
32
38
  */
@@ -44,6 +50,7 @@ export default interface IServerRendererConfiguration {
44
50
  */
45
51
  warmup: boolean;
46
52
  };
53
+
47
54
  /**
48
55
  * Settings for the worker.
49
56
  */
@@ -57,6 +64,7 @@ export default interface IServerRendererConfiguration {
57
64
  */
58
65
  maxConcurrency: number;
59
66
  };
67
+
60
68
  /**
61
69
  * Settings for rendering.
62
70
  */
@@ -89,11 +97,21 @@ export default interface IServerRendererConfiguration {
89
97
  * Disable polyfills used for unimplemented functionality.
90
98
  */
91
99
  disablePolyfills: boolean;
100
+ /**
101
+ * Setup script to be injected before rendering.
102
+ */
103
+ setupScript: string | null;
104
+ /**
105
+ * Rendering mode.
106
+ */
107
+ mode: ServerRendererModeEnum;
92
108
  };
109
+
93
110
  /**
94
- * List of URLs to render.
111
+ * List render items. Each item can be a URL string or an object specifying the URL or HTML string along with additional options.
95
112
  */
96
- urls: Array<string | IServerRendererItem> | null;
113
+ renderItems: Array<string | IServerRendererItem> | null;
114
+
97
115
  /**
98
116
  * Proxy server settings.
99
117
  */
@@ -1,5 +1,6 @@
1
1
  export default interface IServerRendererItem {
2
- url: string;
2
+ url?: string;
3
+ html?: string;
3
4
  outputFile?: string | null;
4
5
  headers?: string[][] | { [key: string]: string } | null;
5
6
  }
@@ -1,9 +1,9 @@
1
1
  export default interface IServerRendererResult {
2
- url: string;
2
+ url: string | null;
3
3
  content: string | null;
4
- status: number;
5
- statusText: string;
6
- headers: { [key: string]: string };
4
+ status: number | null;
5
+ statusText: string | null;
6
+ headers: { [key: string]: string } | null;
7
7
  outputFile: string | null;
8
8
  error: string | null;
9
9
  pageErrors: string[];
@@ -73,11 +73,12 @@ export default class BrowserWindowPolyfill {
73
73
  bufferSubData: () => {},
74
74
  drawElementsInstanced: () => {}
75
75
  });
76
- (<any>window).Worker = class {
76
+ (<any>window).Worker = class Worker {
77
77
  public postMessage(): any {}
78
78
  public terminate(): any {}
79
79
  };
80
- (<any>window).Path2D = class {
80
+
81
+ (<any>window).Path2D = class Path2D {
81
82
  public addPath(): any {}
82
83
  public addPath2D(): any {}
83
84
  public closePath(): any {}
@@ -126,15 +126,15 @@ export default [
126
126
  '1'
127
127
  ],
128
128
  [
129
- '--browser.disableJavaScriptEvaluation',
130
- '',
129
+ '--browser.enableJavaScriptEvaluation',
130
+ '--javaScript, --javascript, -j',
131
131
  'boolean',
132
- 'Disables JavaScript evaluation.',
132
+ 'Enables JavaScript evaluation.',
133
133
  'false'
134
134
  ],
135
135
  [
136
136
  '--browser.suppressInsecureJavaScriptEnvironmentWarning',
137
- '',
137
+ '--suppressJavaScriptWarning, -sj',
138
138
  'boolean',
139
139
  'Suppresses the warning that is printed when code generation from strings is enabled at process level',
140
140
  'false'
@@ -198,6 +198,27 @@ export default [
198
198
  'Setup a virtual server. E.g. -vs="^https://e.com/[a-z]{2}/|./build"',
199
199
  ''
200
200
  ],
201
+ [
202
+ '--browser.module.resolveNodeModules.directory=<path>',
203
+ '',
204
+ 'string',
205
+ 'Directory to resolve node modules from.',
206
+ ''
207
+ ],
208
+ [
209
+ '--browser.module.resolveNodeModules.url=<url>',
210
+ '',
211
+ 'string',
212
+ 'URL to resolve node modules to.',
213
+ ''
214
+ ],
215
+ [
216
+ '--browser.module.resolveNodeModules.mainFields=<fields>',
217
+ '',
218
+ 'string[]',
219
+ 'Comma-separated list of main fields to use when resolving node modules.',
220
+ ''
221
+ ],
201
222
  [
202
223
  '--browser.navigation.disableMainFrameNavigation',
203
224
  '',
@@ -1,9 +1,9 @@
1
- import DefaultServerRendererConfiguration from '../config/DefaultServerRendererConfiguration.js';
2
1
  import IServerRendererConfiguration from '../types/IServerRendererConfiguration.js';
3
2
  import IServerRendererItem from '../types/IServerRendererItem.js';
4
3
  import Path from 'path';
5
4
  import { BrowserNavigationCrossOriginPolicyEnum } from 'happy-dom';
6
5
  import ServerRendererLogLevelEnum from '../enums/ServerRendererLogLevelEnum.js';
6
+ import ServerRendererConfigurationFactory from './ServerRendererConfigurationFactory.js';
7
7
 
8
8
  /**
9
9
  * CLI process arguments parser.
@@ -15,33 +15,72 @@ export default class ProcessArgumentsParser {
15
15
  * @param args Arguments.
16
16
  */
17
17
  public static async getConfiguration(args: string[]): Promise<IServerRendererConfiguration> {
18
- let config: IServerRendererConfiguration = JSON.parse(
19
- JSON.stringify(DefaultServerRendererConfiguration)
20
- );
18
+ let config: IServerRendererConfiguration =
19
+ ServerRendererConfigurationFactory.createConfiguration();
21
20
 
22
21
  for (const arg of args) {
23
22
  if (arg[0] === '-') {
24
23
  if (arg.startsWith('--config=') || arg.startsWith('-c=')) {
25
- config = (await import(Path.resolve(this.stripQuotes(arg.split('=')[1])))).default;
24
+ config = ServerRendererConfigurationFactory.createConfiguration(
25
+ (await import(Path.resolve(this.stripQuotes(arg.split('=')[1])))).default
26
+ );
26
27
 
27
- if (config.urls) {
28
- const newUrls: IServerRendererItem[] = [];
29
- for (const urlItem of config.urls) {
30
- const isString = typeof urlItem === 'string';
31
- const url = new URL(isString ? urlItem : urlItem.url);
32
- newUrls.push({
33
- url: url.href,
34
- outputFile: this.getOutputFile(url),
35
- headers: isString ? null : (<IServerRendererItem>urlItem).headers || null
36
- });
28
+ if (config.renderItems) {
29
+ const newRenderItems: IServerRendererItem[] = [];
30
+ for (const renderItem of config.renderItems) {
31
+ if (typeof renderItem === 'string') {
32
+ const url = new URL(renderItem);
33
+ newRenderItems.push({
34
+ url: url.href,
35
+ outputFile: this.getOutputFile(url)
36
+ });
37
+ } else {
38
+ if (renderItem.url) {
39
+ const url = new URL(renderItem.url);
40
+ const newRenderItem: IServerRendererItem = {
41
+ url: url.href,
42
+ outputFile: renderItem.outputFile || this.getOutputFile(url)
43
+ };
44
+ if (renderItem.html) {
45
+ newRenderItem.html = renderItem.html;
46
+ }
47
+ if (renderItem.headers) {
48
+ newRenderItem.headers = renderItem.headers;
49
+ }
50
+ newRenderItems.push(newRenderItem);
51
+ } else {
52
+ if (!renderItem.outputFile) {
53
+ throw new Error(
54
+ `Missing output file in render item: ${JSON.stringify(renderItem)}`
55
+ );
56
+ }
57
+ const newRenderItem: IServerRendererItem = {
58
+ html: renderItem.html,
59
+ outputFile: renderItem.outputFile
60
+ };
61
+ if (renderItem.headers) {
62
+ newRenderItem.headers = renderItem.headers;
63
+ }
64
+ newRenderItems.push(newRenderItem);
65
+ }
66
+ }
37
67
  }
38
- config.urls = newUrls;
68
+ config.renderItems = newRenderItems;
39
69
  }
40
70
  } else if (arg === '--help' || arg === '-h') {
41
71
  config.help = true;
42
- } else if (arg === '--browser.disableJavaScriptEvaluation') {
43
- config.browser.enableJavaScriptEvaluation = false;
44
- } else if (arg === '--browser.suppressInsecureJavaScriptEnvironmentWarning') {
72
+ } else if (
73
+ arg === '--browser.enableJavaScriptEvaluation' ||
74
+ arg === '--javaScript' ||
75
+ arg === '--javascript' ||
76
+ arg === '-j'
77
+ ) {
78
+ config.browser.enableJavaScriptEvaluation = true;
79
+ } else if (
80
+ arg === '--browser.suppressInsecureJavaScriptEnvironmentWarning' ||
81
+ arg === '--suppressJavaScriptWarning' ||
82
+ arg === '-sj'
83
+ ) {
45
84
  config.browser.suppressInsecureJavaScriptEnvironmentWarning = true;
46
85
  } else if (arg === '--browser.disableJavaScriptFileLoading') {
47
86
  config.browser.disableJavaScriptFileLoading = true;
@@ -126,6 +165,32 @@ export default class ProcessArgumentsParser {
126
165
  url: new RegExp(parts[0].trim()),
127
166
  directory: parts[1].trim()
128
167
  });
168
+ } else if (arg.startsWith('--browser.module.resolveNodeModules.url=')) {
169
+ if (!config.browser.module.resolveNodeModules) {
170
+ config.browser.module.resolveNodeModules = {
171
+ url: '',
172
+ directory: ''
173
+ };
174
+ }
175
+ config.browser.module.resolveNodeModules.url = this.stripQuotes(arg.split('=')[1]);
176
+ } else if (arg.startsWith('--browser.module.resolveNodeModules.directory=')) {
177
+ if (!config.browser.module.resolveNodeModules) {
178
+ config.browser.module.resolveNodeModules = {
179
+ url: '',
180
+ directory: ''
181
+ };
182
+ }
183
+ config.browser.module.resolveNodeModules.directory = this.stripQuotes(arg.split('=')[1]);
184
+ } else if (arg.startsWith('--browser.module.resolveNodeModules.mainFields=')) {
185
+ if (!config.browser.module.resolveNodeModules) {
186
+ config.browser.module.resolveNodeModules = {
187
+ url: '',
188
+ directory: ''
189
+ };
190
+ }
191
+ config.browser.module.resolveNodeModules.mainFields = this.stripQuotes(
192
+ arg.split('=')[1]
193
+ ).split(',');
129
194
  } else if (arg === '--browser.navigation.disableMainFrameNavigation') {
130
195
  config.browser.navigation.disableMainFrameNavigation = true;
131
196
  } else if (arg === '--browser.navigation.disableChildFrameNavigation') {
@@ -189,7 +254,7 @@ export default class ProcessArgumentsParser {
189
254
  } else if (arg === '--cache.disable') {
190
255
  config.cache.disable = true;
191
256
  } else if (arg.startsWith('--cache.directory=') || arg.startsWith('-cd=')) {
192
- config.cache.directory = this.stripQuotes(arg.split('=')[1]);
257
+ config.cache.directory = Path.resolve(this.stripQuotes(arg.split('=')[1]));
193
258
  } else if (arg === '--cache.warmup') {
194
259
  config.cache.warmup = true;
195
260
  } else if (arg.startsWith('--logLevel=') || arg.startsWith('-l=')) {
@@ -234,7 +299,7 @@ export default class ProcessArgumentsParser {
234
299
  } else if (arg === '--inspect' || arg === '-i') {
235
300
  config.inspect = true;
236
301
  } else if (arg.startsWith('--outputDirectory=') || arg.startsWith('-o=')) {
237
- config.outputDirectory = this.stripQuotes(arg.split('=')[1]);
302
+ config.outputDirectory = Path.resolve(this.stripQuotes(arg.split('=')[1]));
238
303
  } else if (arg.startsWith('--server.serverURL=') || arg.startsWith('-su=')) {
239
304
  config.server.serverURL = this.stripQuotes(arg.split('=')[1]);
240
305
  } else if (arg.startsWith('--server.targetOrigin=') || arg.startsWith('-st=')) {
@@ -255,6 +320,10 @@ export default class ProcessArgumentsParser {
255
320
  throw new Error(
256
321
  'URLs shouldn\'t be set by "--urls=". Instead set them with quotes without an argument name. E.g. "https://example.com/page" "https://example.com/another/page"'
257
322
  );
323
+ } else if (arg.startsWith('--renderItems=')) {
324
+ throw new Error(
325
+ 'Render items shouldn\'t be set by "--renderItems=". Instead set each URL with quotes without an argument name. E.g. "https://example.com/page" "https://example.com/another/page"'
326
+ );
258
327
  }
259
328
  } else if (arg) {
260
329
  const urlString =
@@ -274,11 +343,11 @@ export default class ProcessArgumentsParser {
274
343
  outputFile: this.getOutputFile(url)
275
344
  };
276
345
 
277
- if (!config.urls) {
278
- config.urls = [];
346
+ if (!config.renderItems) {
347
+ config.renderItems = [];
279
348
  }
280
349
 
281
- config.urls.push(item);
350
+ config.renderItems.push(item);
282
351
  }
283
352
  }
284
353
  }
@@ -40,7 +40,7 @@ export default class ServerRendererConfigurationFactory {
40
40
  ...DefaultServerRendererConfiguration.render,
41
41
  ...configuration?.render
42
42
  },
43
- urls: configuration?.urls || null,
43
+ renderItems: configuration?.renderItems || null,
44
44
  server: {
45
45
  ...DefaultServerRendererConfiguration.server,
46
46
  ...configuration?.server
@@ -1 +0,0 @@
1
- {"version":3,"file":"ServerRendererWorker.d.ts","sourceRoot":"","sources":["../src/ServerRendererWorker.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACxC;;OAEG;WACiB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAe5C"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ServerRendererWorker.js","sourceRoot":"","sources":["../src/ServerRendererWorker.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAEvC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACxC;;OAEG;IACI,MAAM,CAAC,KAAK,CAAC,OAAO;QAC1B,MAAM,EAAE,aAAa,EAAE,GAAG,UAAU,CAAC;QAErC,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;YAC3B,SAAS,CAAC,IAAI,EAAE,CAAC;YACjB,SAAS,CAAC,eAAe,EAAE,CAAC;QAC7B,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,qBAAqB,CAAC,aAAa,CAAC,CAAC;QAEzD,UAAU,EAAE,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YAC7C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5C,UAAU,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;IACJ,CAAC;CACD;AAED,oBAAoB,CAAC,OAAO,EAAE,CAAC"}