@oliversalzburg/js-utils 0.0.28-dev.10 → 0.0.28-dev.101

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 CHANGED
@@ -9,3 +9,5 @@ A collection of random utilities for JS/TS development.
9
9
  - Tree-shaking in mind. Subpath-imports fully supported and recommended.
10
10
 
11
11
  - No runtime dependencies.
12
+
13
+ - All runtime code is licensed under MIT, or fully compatible licenses.
@@ -0,0 +1,269 @@
1
+ /// <reference types="@types/web" />
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ /// <reference types="node" resolution-mode="require"/>
4
+ import { ConstructorOf } from "../core.js";
5
+ import { Random } from "../random.js";
6
+ import { CanvasSandboxApplication, CanvasSandboxExpectedOptions } from "./canvas-sandbox.js";
7
+ import { Canvas } from "./canvas.js";
8
+ import { Canvas2DHeadless } from "./canvas2d-headless.js";
9
+ import { RenderLoop } from "./render-loop.js";
10
+ /**
11
+ * Sent from the host when a worker should reconfigure itself.
12
+ * @group Graphics
13
+ */
14
+ export interface CanvasWorkerMessageReconfigure<TApplicationOptions extends CanvasSandboxExpectedOptions> {
15
+ /**
16
+ * Type identifier of this message.
17
+ */
18
+ type: "reconfigure";
19
+ /**
20
+ * New ID for the worker.
21
+ */
22
+ id: string;
23
+ /**
24
+ * New canvas for the worker to use.
25
+ */
26
+ canvas: OffscreenCanvas;
27
+ /**
28
+ * New application options to use.
29
+ */
30
+ options: TApplicationOptions;
31
+ }
32
+ /**
33
+ * Sent from the host when a worker should start its rendering kernel.
34
+ * @group Graphics
35
+ */
36
+ export interface CanvasWorkerMessageStart<TApplicationOptions extends CanvasSandboxExpectedOptions> {
37
+ /**
38
+ * Type identifier of this message.
39
+ */
40
+ type: "start";
41
+ /**
42
+ * New application options to use before starting.
43
+ */
44
+ options: TApplicationOptions;
45
+ }
46
+ /**
47
+ * Sent from the host when a worker should un/pause rendering.
48
+ * @group Graphics
49
+ */
50
+ export interface CanvasWorkerMessagePause {
51
+ /**
52
+ * Type identifier of this message.
53
+ */
54
+ type: "pause";
55
+ /**
56
+ * Should the worker pause (`true`) or unpause (`false`)?
57
+ */
58
+ pause: boolean;
59
+ }
60
+ /**
61
+ * Sent from a worker when it finished rendering the current scene.
62
+ * @group Graphics
63
+ */
64
+ export interface CanvasWorkerSceneFinishMessage {
65
+ /**
66
+ * Type identifier of this message.
67
+ */
68
+ type: "sceneFinish";
69
+ /**
70
+ * Point in time when the scene finished rendering.
71
+ */
72
+ timestamp: number;
73
+ }
74
+ /**
75
+ * The messages that are passed between the canvas sandbox host and its workers.
76
+ * @group Graphics
77
+ */
78
+ export type CanvasWorkerMessage<TApplicationOptions extends CanvasSandboxExpectedOptions> = CanvasWorkerMessageReconfigure<TApplicationOptions> | CanvasWorkerMessageStart<TApplicationOptions> | CanvasWorkerSceneFinishMessage | CanvasWorkerMessagePause | {
79
+ type: string;
80
+ };
81
+ /**
82
+ * The canvas worker handles a web worker instance in the canvas sandbox host.
83
+ *
84
+ * It provides a thin abstraction over the web worker IPC messaging.
85
+ * @group Graphics
86
+ */
87
+ export declare class CanvasWorker<TApplicationOptions extends CanvasSandboxExpectedOptions> extends EventTarget {
88
+ /**
89
+ * The ID of this worker.
90
+ */
91
+ id: string;
92
+ /**
93
+ * The canvas element that this worker should handle.
94
+ * This is purely for informational purposes on the host side.
95
+ */
96
+ canvas: HTMLCanvasElement | undefined;
97
+ /**
98
+ * The offscreen canvas that we created for this worker.
99
+ */
100
+ canvasOffscreen: OffscreenCanvas | undefined;
101
+ /**
102
+ * The application options for the worker.
103
+ *
104
+ * These are usually very similar, if not identical, to the host application options.
105
+ * Workers just usually get a different viewport assigned to them, correlating with the
106
+ * canvas element in the DOM that they draw to.
107
+ */
108
+ readonly options: TApplicationOptions;
109
+ /**
110
+ * The web worker instance itself.
111
+ */
112
+ readonly workerInstance: Worker;
113
+ /**
114
+ * Constructs a new canvas worker.
115
+ * @param id - The ID of the worker.
116
+ * @param source - The code that the worker should run. It's common to use a hybrid code module
117
+ * for as the sandbox application. This means the code of the host and the workers is identical,
118
+ * they just construct different code path on load/init. In such case, the source can simply be
119
+ * `new URL(import.meta.url)`.
120
+ * @param canvas - The canvas element to draw to.
121
+ * @param options - The application options.
122
+ */
123
+ constructor(id: string, source: URL, canvas: HTMLCanvasElement, options: TApplicationOptions);
124
+ /**
125
+ * Posts a message to the worker.
126
+ *
127
+ * As messages to workers are _posted_ instead of _sent_, there is no delivery feedback.
128
+ * @param message - The message to post.
129
+ * @param transfer - The transferables to transfer to the worker with this message.
130
+ */
131
+ postMessage(message: CanvasWorkerMessage<TApplicationOptions>, transfer?: Array<Transferable>): void;
132
+ }
133
+ /**
134
+ * The canvas worker instance handles a web worker instance in worker instance itself, and
135
+ * handles a feedback channel to the host application.
136
+ *
137
+ * It provides a thin abstraction over the web worker IPC messaging.
138
+ *
139
+ * ```ts
140
+ * // Construct inside of worker from global scope and rendering kernel class.
141
+ * const worker = new CanvasWorkerInstance(self, RenderKernel);
142
+ * // Listen to custom events (messages) you send from the host.
143
+ * worker.addEventListener("fade", () => worker.renderKernel?.fadeOut());
144
+ * ```
145
+ * @group Graphics
146
+ */
147
+ export declare class CanvasWorkerInstance<TCanvas extends Canvas2DHeadless, TApplicationOptions extends CanvasSandboxExpectedOptions, TKernel extends CanvasSandboxApplication<TCanvas, TApplicationOptions>> extends EventTarget {
148
+ #private;
149
+ /**
150
+ * The global scope of the worker.
151
+ */
152
+ readonly self: typeof globalThis;
153
+ /**
154
+ * The ID of the worker.
155
+ */
156
+ id: string;
157
+ /**
158
+ * The rendering kernel, which produces frames, and renders them to our
159
+ * offscreen canvas.
160
+ */
161
+ renderKernel: TKernel | undefined;
162
+ /**
163
+ * The canvas element we're presenting to.
164
+ */
165
+ canvas: TCanvas | undefined;
166
+ /**
167
+ * The offscreen canvas we're rendering to.
168
+ */
169
+ offscreenCanvas: OffscreenCanvas | undefined;
170
+ /**
171
+ * Our rendering context to draw to.
172
+ */
173
+ offscreenContext: OffscreenCanvasRenderingContext2D | undefined;
174
+ /**
175
+ * Manages frame rendering timing.
176
+ */
177
+ renderLoop: RenderLoop | undefined;
178
+ /**
179
+ * Constructs a new canvas worker instance.
180
+ *
181
+ * Note that many properties are only initialized after the host sent an
182
+ * initialization message.
183
+ * @param self - Our global worker scope.
184
+ * @param Kernel - The rendering kernel class to construct.
185
+ */
186
+ constructor(self: typeof globalThis, Kernel: ConstructorOf<TKernel>);
187
+ /**
188
+ * Render a frame.
189
+ * @param delta - How many milliseconds have passed since the last invocation?
190
+ */
191
+ render: (delta: number) => void;
192
+ /**
193
+ * Reconfigure the worker.
194
+ * @param id - New ID for this worker.
195
+ * @param offscreenCanvas - New canvas to render to.
196
+ * @param options - New application options.
197
+ */
198
+ reconfigure(id: string, offscreenCanvas: OffscreenCanvas, options: TApplicationOptions): void;
199
+ /**
200
+ * Start rendering.
201
+ * @param options - New application options.
202
+ */
203
+ start(options: TApplicationOptions): void;
204
+ /**
205
+ * Posts a message to the host.
206
+ * @param message - The message to post.
207
+ */
208
+ postMessage(message: CanvasWorkerMessage<TApplicationOptions>): void;
209
+ }
210
+ /**
211
+ * Base class for multi-process canvas sandbox applications.
212
+ *
213
+ * Helps in orchestrating the workers.
214
+ * @group Graphics
215
+ */
216
+ export declare abstract class CanvasSandboxHostApplication<TCanvas extends Canvas, TApplicationOptions extends CanvasSandboxExpectedOptions> implements CanvasSandboxApplication<TCanvas, TApplicationOptions> {
217
+ /**
218
+ * Usually a hidden canvas that serves as a proxy for canvas sandbox events.
219
+ */
220
+ canvas: TCanvas;
221
+ /**
222
+ * Application options.
223
+ */
224
+ options: TApplicationOptions;
225
+ /**
226
+ * PRNG instance for the application.
227
+ */
228
+ random: Random;
229
+ /**
230
+ * Is the application running right now?
231
+ */
232
+ paused: boolean;
233
+ /**
234
+ * Web worker instances.
235
+ */
236
+ readonly workers: CanvasWorker<TApplicationOptions>[];
237
+ /**
238
+ * Construct a new host application.
239
+ * @param canvas - Usually a hidden canvas in the DOM to serve as an event proxy.
240
+ * @param options - Application options.
241
+ */
242
+ constructor(canvas: TCanvas, options: TApplicationOptions);
243
+ /**
244
+ * Reconfigure the application with the given options and restart it.
245
+ * @param canvas - The canvas to use.
246
+ * @param options - The new options to use.
247
+ */
248
+ abstract reconfigure(canvas: TCanvas, options?: Partial<TApplicationOptions>): void;
249
+ /**
250
+ * Request the application to draw a new frame.
251
+ * This call has no effect in a host application, as all rendering happens in the workers.
252
+ * @param _delta - How many milliseconds passed since the last frame draw.
253
+ * @param _timestamp - The current timestamp.
254
+ */
255
+ onDraw(_delta: number, _timestamp: number): void;
256
+ /**
257
+ * Pauses all workers.
258
+ */
259
+ pause(): void;
260
+ /**
261
+ * Start the application.
262
+ */
263
+ start(): void;
264
+ /**
265
+ * Posts a message to all workers.
266
+ * @param message - Message to post to all workers.
267
+ */
268
+ postMessageAll(message: CanvasWorkerMessage<TApplicationOptions>): void;
269
+ }
@@ -0,0 +1,282 @@
1
+ import { isNil, mustExist } from "../nil.js";
2
+ import { Random } from "../random.js";
3
+ import { Canvas2DHeadless } from "./canvas2d-headless.js";
4
+ import { RenderLoop } from "./render-loop.js";
5
+ /**
6
+ * The canvas worker handles a web worker instance in the canvas sandbox host.
7
+ *
8
+ * It provides a thin abstraction over the web worker IPC messaging.
9
+ * @group Graphics
10
+ */
11
+ export class CanvasWorker extends EventTarget {
12
+ /**
13
+ * The ID of this worker.
14
+ */
15
+ id;
16
+ /**
17
+ * The canvas element that this worker should handle.
18
+ * This is purely for informational purposes on the host side.
19
+ */
20
+ canvas;
21
+ /**
22
+ * The offscreen canvas that we created for this worker.
23
+ */
24
+ canvasOffscreen;
25
+ /**
26
+ * The application options for the worker.
27
+ *
28
+ * These are usually very similar, if not identical, to the host application options.
29
+ * Workers just usually get a different viewport assigned to them, correlating with the
30
+ * canvas element in the DOM that they draw to.
31
+ */
32
+ options;
33
+ /**
34
+ * The web worker instance itself.
35
+ */
36
+ workerInstance;
37
+ /**
38
+ * Constructs a new canvas worker.
39
+ * @param id - The ID of the worker.
40
+ * @param source - The code that the worker should run. It's common to use a hybrid code module
41
+ * for as the sandbox application. This means the code of the host and the workers is identical,
42
+ * they just construct different code path on load/init. In such case, the source can simply be
43
+ * `new URL(import.meta.url)`.
44
+ * @param canvas - The canvas element to draw to.
45
+ * @param options - The application options.
46
+ */
47
+ constructor(id, source, canvas, options) {
48
+ super();
49
+ this.id = id;
50
+ this.canvas = canvas;
51
+ this.options = options;
52
+ this.workerInstance = new Worker(source, {
53
+ type: "module",
54
+ });
55
+ // Translate messages to events.
56
+ this.workerInstance.onmessage = (message) => this.dispatchEvent(new CustomEvent(message.data.type));
57
+ }
58
+ /**
59
+ * Posts a message to the worker.
60
+ *
61
+ * As messages to workers are _posted_ instead of _sent_, there is no delivery feedback.
62
+ * @param message - The message to post.
63
+ * @param transfer - The transferables to transfer to the worker with this message.
64
+ */
65
+ postMessage(message, transfer) {
66
+ this.workerInstance.postMessage(message, transfer ?? []);
67
+ }
68
+ }
69
+ /**
70
+ * The canvas worker instance handles a web worker instance in worker instance itself, and
71
+ * handles a feedback channel to the host application.
72
+ *
73
+ * It provides a thin abstraction over the web worker IPC messaging.
74
+ *
75
+ * ```ts
76
+ * // Construct inside of worker from global scope and rendering kernel class.
77
+ * const worker = new CanvasWorkerInstance(self, RenderKernel);
78
+ * // Listen to custom events (messages) you send from the host.
79
+ * worker.addEventListener("fade", () => worker.renderKernel?.fadeOut());
80
+ * ```
81
+ * @group Graphics
82
+ */
83
+ export class CanvasWorkerInstance extends EventTarget {
84
+ /**
85
+ * The global scope of the worker.
86
+ */
87
+ self;
88
+ /**
89
+ * The ID of the worker.
90
+ */
91
+ id;
92
+ /**
93
+ * The rendering kernel, which produces frames, and renders them to our
94
+ * offscreen canvas.
95
+ */
96
+ renderKernel;
97
+ /**
98
+ * The canvas element we're presenting to.
99
+ */
100
+ canvas;
101
+ /**
102
+ * The offscreen canvas we're rendering to.
103
+ */
104
+ offscreenCanvas;
105
+ /**
106
+ * Our rendering context to draw to.
107
+ */
108
+ offscreenContext;
109
+ /**
110
+ * Manages frame rendering timing.
111
+ */
112
+ renderLoop;
113
+ /**
114
+ * Constructor of our rendering kernel.
115
+ */
116
+ #Kernel;
117
+ /**
118
+ * Constructs a new canvas worker instance.
119
+ *
120
+ * Note that many properties are only initialized after the host sent an
121
+ * initialization message.
122
+ * @param self - Our global worker scope.
123
+ * @param Kernel - The rendering kernel class to construct.
124
+ */
125
+ constructor(self, Kernel) {
126
+ super();
127
+ this.self = self;
128
+ this.id = "<unassigned>";
129
+ this.#Kernel = Kernel;
130
+ self.onmessage = (message) => {
131
+ const event = new CustomEvent(message.data.type);
132
+ this.dispatchEvent(event);
133
+ if (event.defaultPrevented) {
134
+ return;
135
+ }
136
+ if (message.data.type === "start") {
137
+ const startMessage = message.data;
138
+ this.start(startMessage.options);
139
+ }
140
+ else if (message.data.type === "pause") {
141
+ const pauseMessage = message.data;
142
+ this.renderKernel?.pause(pauseMessage.pause);
143
+ }
144
+ else if (message.data.type === "reconfigure") {
145
+ const reconfigureMessage = message.data;
146
+ this.reconfigure(reconfigureMessage.id, reconfigureMessage.canvas, reconfigureMessage.options);
147
+ }
148
+ };
149
+ }
150
+ /**
151
+ * Render a frame.
152
+ * @param delta - How many milliseconds have passed since the last invocation?
153
+ */
154
+ render = (delta) => {
155
+ this.renderKernel?.onDraw(delta, new Date().valueOf());
156
+ };
157
+ /**
158
+ * Reconfigure the worker.
159
+ * @param id - New ID for this worker.
160
+ * @param offscreenCanvas - New canvas to render to.
161
+ * @param options - New application options.
162
+ */
163
+ reconfigure(id, offscreenCanvas, options) {
164
+ this.renderLoop?.block();
165
+ this.id = id;
166
+ this.offscreenCanvas = offscreenCanvas;
167
+ this.offscreenContext = mustExist(this.offscreenCanvas.getContext("2d"));
168
+ this.canvas = new Canvas2DHeadless(this.offscreenCanvas, this.offscreenContext);
169
+ this.renderLoop = new RenderLoop(this.render, this.canvas);
170
+ if (isNil(this.renderKernel)) {
171
+ this.renderKernel = new this.#Kernel(this, this.canvas, options);
172
+ }
173
+ else {
174
+ this.renderKernel.reconfigure(this.canvas, options);
175
+ }
176
+ }
177
+ /**
178
+ * Start rendering.
179
+ * @param options - New application options.
180
+ */
181
+ start(options) {
182
+ if (!this.renderKernel) {
183
+ return;
184
+ }
185
+ this.renderKernel.random = new Random(options.seed);
186
+ this.renderKernel.start(options);
187
+ this.renderLoop?.unblock();
188
+ }
189
+ /**
190
+ * Posts a message to the host.
191
+ * @param message - The message to post.
192
+ */
193
+ postMessage(message) {
194
+ this.self.postMessage(message);
195
+ }
196
+ }
197
+ /**
198
+ * Base class for multi-process canvas sandbox applications.
199
+ *
200
+ * Helps in orchestrating the workers.
201
+ * @group Graphics
202
+ */
203
+ export class CanvasSandboxHostApplication {
204
+ /**
205
+ * Usually a hidden canvas that serves as a proxy for canvas sandbox events.
206
+ */
207
+ canvas;
208
+ /**
209
+ * Application options.
210
+ */
211
+ options;
212
+ /**
213
+ * PRNG instance for the application.
214
+ */
215
+ random;
216
+ /**
217
+ * Is the application running right now?
218
+ */
219
+ paused = false;
220
+ /**
221
+ * Web worker instances.
222
+ */
223
+ workers = new Array();
224
+ /**
225
+ * Construct a new host application.
226
+ * @param canvas - Usually a hidden canvas in the DOM to serve as an event proxy.
227
+ * @param options - Application options.
228
+ */
229
+ constructor(canvas, options) {
230
+ this.options = options;
231
+ this.canvas = canvas;
232
+ this.random = new Random(options.seed);
233
+ this.reconfigure(canvas, options);
234
+ }
235
+ /**
236
+ * Request the application to draw a new frame.
237
+ * This call has no effect in a host application, as all rendering happens in the workers.
238
+ * @param _delta - How many milliseconds passed since the last frame draw.
239
+ * @param _timestamp - The current timestamp.
240
+ */
241
+ onDraw(_delta, _timestamp) {
242
+ // Draws happen in workers.
243
+ }
244
+ /**
245
+ * Pauses all workers.
246
+ */
247
+ pause() {
248
+ this.paused = !this.paused;
249
+ for (const worker of this.workers) {
250
+ worker.postMessage({
251
+ type: "pause",
252
+ pause: this.paused,
253
+ });
254
+ }
255
+ }
256
+ /**
257
+ * Start the application.
258
+ */
259
+ start() {
260
+ this.paused = false;
261
+ for (const worker of this.workers) {
262
+ worker.postMessage({
263
+ type: "start",
264
+ options: {
265
+ ...this.options,
266
+ seed: this.random.seed,
267
+ viewport: worker.options.viewport,
268
+ },
269
+ });
270
+ }
271
+ }
272
+ /**
273
+ * Posts a message to all workers.
274
+ * @param message - Message to post to all workers.
275
+ */
276
+ postMessageAll(message) {
277
+ for (const worker of this.workers) {
278
+ worker.postMessage(message);
279
+ }
280
+ }
281
+ }
282
+ //# sourceMappingURL=canvas-sandbox-mp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-sandbox-mp.js","sourceRoot":"","sources":["../../source/graphics/canvas-sandbox-mp.ts"],"names":[],"mappings":"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"}
@@ -38,6 +38,28 @@ export interface CanvasSandboxExpectedOptions {
38
38
  * The seed for the PRNG.
39
39
  */
40
40
  seed: number;
41
+ /**
42
+ * The part of the canvas that the targetted render kernel will handle.
43
+ * This is ignored, unless you use multi-process rendering.
44
+ */
45
+ viewport: {
46
+ /**
47
+ * Start of the viewport in fractional world-space.
48
+ */
49
+ x: number;
50
+ /**
51
+ * Start of the viewport in fractional world-space.
52
+ */
53
+ y: number;
54
+ /**
55
+ * Width of the viewport in fractional world-space.
56
+ */
57
+ w: number;
58
+ /**
59
+ * Height of the viewport in fractional world-space.
60
+ */
61
+ h: number;
62
+ };
41
63
  }
42
64
  /**
43
65
  * Describes an application running inside the {@linkcode CanvasSandbox}.
@@ -74,7 +96,12 @@ export interface CanvasSandboxApplication<TCanvas extends Canvas, TApplicationOp
74
96
  /**
75
97
  * Start the application.
76
98
  */
77
- start(): void;
99
+ start(options?: Partial<TApplicationOptions>): void;
100
+ /**
101
+ * Pause rendering.
102
+ * @param paused - Should rendering be paused (`true`) or unpaused (`false`)?
103
+ */
104
+ pause(paused: boolean): void;
78
105
  }
79
106
  /**
80
107
  * Options for a {@linkcode CanvasSandbox}.
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable no-console */
2
2
  import { Shake } from "../device/shake.js";
3
3
  import { getDocumentElementTypeById } from "../dom/core.js";
4
- import { nextPalette } from "./core.js";
5
4
  import { RenderLoop } from "./render-loop.js";
6
5
  /**
7
6
  * Provides the input as-is.
@@ -60,6 +59,7 @@ export const CANVAS_SANDBOX_DEFAULT_CSS = /* PURE */ css `
60
59
  bottom: 0;
61
60
  justify-content: center;
62
61
  align-items: center;
62
+ z-index: 1;
63
63
 
64
64
  filter: drop-shadow(10px 10px 4px rgba(0, 0, 0, 0.5));
65
65
  transition: all 1s;
@@ -271,14 +271,11 @@ export class CanvasSandbox {
271
271
  switch (event.keyCode) {
272
272
  case 13:
273
273
  // Enter
274
- nextPalette();
275
274
  this.#reconfigureApplication();
276
- this.application.reconfigure(this.canvas);
277
- this.application.start();
278
275
  break;
279
276
  case 32:
280
277
  // Space
281
- this.application.paused = !this.application.paused;
278
+ this.application.pause(!this.application.paused);
282
279
  break;
283
280
  }
284
281
  });
@@ -297,18 +294,8 @@ export class CanvasSandbox {
297
294
  * Create event listeners on the canvas node in the document.
298
295
  */
299
296
  #hookCanvas() {
300
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
301
- this.canvasNode.addEventListener("click", async (event) => {
302
- if (this.document.fullscreenElement) {
303
- if (this.sandboxOptions.devMode) {
304
- console.info("CanvasSandbox: Exiting fullscreen mode...");
305
- }
306
- await document.exitFullscreen();
307
- return;
308
- }
309
- // Run the next variation of the application.
310
- nextPalette();
311
- this.#reconfigureApplication();
297
+ this.canvasNode.addEventListener("click", (event) => {
298
+ this.application.pause(!this.application.paused);
312
299
  event.preventDefault();
313
300
  });
314
301
  }
@@ -321,8 +308,7 @@ export class CanvasSandbox {
321
308
  .then(() => {
322
309
  this.shakeHandler.addEventListener("shake", () => {
323
310
  console.info("CanvasSandbox: Shake detected. Reconfiguring application...");
324
- this.application.reconfigure(this.canvas);
325
- this.application.start();
311
+ this.#reconfigureApplication();
326
312
  });
327
313
  })
328
314
  .catch(console.error);