@sealcode/sealgen 0.17.14 → 0.17.15

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.
@@ -1,7 +1,7 @@
1
1
  import { Builder } from "./builder.js";
2
2
  export declare const COLORS_TS_PATH = "src/back/colors.ts";
3
3
  export declare const COLORS_CSS_PATH = "src/colors.css";
4
- export declare const COLORS_HTML_PATH = "src/colors.html";
4
+ export declare const COLORS_HTML_PATH = "public/dist/colors.html";
5
5
  export declare class ColorsBuilder extends Builder {
6
6
  getName(): string;
7
7
  ownsFile(file_path: string): boolean;
@@ -1 +1,2 @@
1
- export declare function make_notifier(port: number): (message: string) => void;
1
+ export declare const APP_BACK_ALIVE_SIGNAL = "app-back-alive";
2
+ export declare function make_notifier(port: number, app_port: number): (message: string) => void;
package/lib/build.js CHANGED
@@ -88,7 +88,7 @@ async function build(watch) {
88
88
  if (watch) {
89
89
  const watcher = chokidar.watch("src", { ignoreInitial: true });
90
90
  const port = await get_notifier_port();
91
- const notifier = make_notifier(port);
91
+ const notifier = make_notifier(port, 8080);
92
92
  await write_notifier_config(watch, port);
93
93
  watcher.on("all", (_, file_path) => {
94
94
  console.log("Detected a change!", file_path);
@@ -6,9 +6,10 @@ import { colord } from "colord";
6
6
  import { formatWithPrettier } from "../utils/prettier.js";
7
7
  import { Builder } from "./builder.js";
8
8
  import { exec } from "../utils/exec.js";
9
+ import { APP_BACK_ALIVE_SIGNAL } from "../notifier.js";
9
10
  const COLORS_TS_PATH = "src/back/colors.ts";
10
11
  const COLORS_CSS_PATH = "src/colors.css";
11
- const COLORS_HTML_PATH = "src/colors.html";
12
+ const COLORS_HTML_PATH = "public/dist/colors.html";
12
13
  class ColorsBuilder extends Builder {
13
14
  getName() {
14
15
  return "colors";
@@ -72,15 +73,18 @@ class ColorsBuilder extends Builder {
72
73
  group_name,
73
74
  is_main = false
74
75
  }) {
76
+ const c = colord(color);
75
77
  return (
76
78
  /* HTML */
77
79
  `<div
78
- class="color-box ${colord(color).isDark() ? "is-dark" : ""} ${is_main ? "is-main" : ""}"
80
+ class="color-box ${c.isDark() ? "is-dark" : ""} ${is_main ? "is-main" : ""} ${c.toHsl().l > 90 ? "is-almost-white" : ""}"
79
81
  style="background-color: ${color}; order: ${parseInt(shade_no)}"
80
82
  data-color-name="${name}"
81
83
  data-color-group-name="${group_name}"
82
84
  data-color-shade-number="${shade_no}"
83
- ></div>`
85
+ >
86
+ <span class="hex">${c.toHex()}</span>
87
+ </div>`
84
88
  );
85
89
  }
86
90
  for (const group_name of Object.keys(color_groups).sort(
@@ -126,11 +130,28 @@ class ColorsBuilder extends Builder {
126
130
  }`,
127
131
  "css"
128
132
  );
133
+ function makeDemoPair(fg, bg, text, classname = "") {
134
+ return (
135
+ /* HTML */
136
+ ` <div
137
+ class="pair ${classname}"
138
+ data-fg="${fg}"
139
+ data-bg="${bg}"
140
+ style="background-color: ${bg}; color: ${fg}"
141
+ >
142
+ <span>${text}</span>
143
+ </div>`
144
+ );
145
+ }
129
146
  const html = await formatWithPrettier(
130
147
  /* HTML */
131
148
  `<!DOCTYPE html>
132
149
  <html>
133
150
  <head>
151
+ <title>Color palettes</title>
152
+ <style>
153
+ ${css}
154
+ </style>
134
155
  <style>
135
156
  * {
136
157
  font-family: sans-serif;
@@ -151,6 +172,10 @@ class ColorsBuilder extends Builder {
151
172
  &:hover {
152
173
  transform: scale(1.1);
153
174
  z-index: 2;
175
+
176
+ .hex {
177
+ visibility: visible;
178
+ }
154
179
  }
155
180
 
156
181
  &.is-main {
@@ -159,22 +184,42 @@ class ColorsBuilder extends Builder {
159
184
  margin-top: -8px;
160
185
  box-shadow: 0px 0px 6px 2px white;
161
186
  z-index: 1;
187
+
188
+ &:before {
189
+ font-weight: bold;
190
+ }
191
+ }
192
+
193
+ &.is-almost-white.is-main {
194
+ box-shadow: 0px 0px 6px 1px #00000078;
195
+ }
196
+
197
+ .hex {
198
+ padding: 8px;
199
+ font-family: Menlo, Consolas, Monaco,
200
+ Liberation Mono, Lucida Console,
201
+ monospace;
202
+ opacity: 0.5;
203
+ visibility: hidden;
204
+ }
205
+
206
+ &.is-dark .hex {
207
+ color: white;
162
208
  }
163
209
  }
164
210
 
165
- .color-box:after {
211
+ .color-box:before {
166
212
  content: attr(data-color-shade-number);
167
213
  box-sizing: border-box;
168
214
  display: block;
169
215
  color: black;
170
216
  font-size: 14px;
171
217
  width: 100%;
172
- height: 100%;
173
218
  padding: 10px;
174
219
  opacity: 0.3;
175
220
  }
176
221
 
177
- .color-box.is-dark:after {
222
+ .color-box.is-dark:before {
178
223
  color: white;
179
224
  }
180
225
 
@@ -200,22 +245,88 @@ class ColorsBuilder extends Builder {
200
245
  animation-timing-function: ease-out;
201
246
  z-index: 3;
202
247
  }
248
+
249
+ .container {
250
+ width: 100vw;
251
+ display: flex;
252
+ flex-flow: row wrap;
253
+ gap: 16px;
254
+
255
+ @media (max-width: 1400px) {
256
+ .demo {
257
+ order: 1;
258
+ }
259
+ .pallete {
260
+ order: 2;
261
+ }
262
+ }
263
+
264
+ .canvas {
265
+ background-color: var(--color-brand-canvas);
266
+ display: flex;
267
+ flex-flow: column;
268
+ border: 1px dashed gray;
269
+ color: gray;
270
+
271
+ gap: 8px;
272
+ padding: 8px;
273
+
274
+ & > * {
275
+ padding: 8px;
276
+ color: black;
277
+ }
278
+
279
+ .pair.single span {
280
+ opacity: 0.5;
281
+ }
282
+ }
283
+ }
203
284
  </style>
204
285
  </head>
205
286
  <body>
206
- ${html_colors}
287
+ <div class="container">
288
+ <div class="pallete">${html_colors}</div>
289
+ <div class="demo">
290
+ <div class="canvas">
291
+ <span>canvas</span>
292
+ ${makeDemoPair(
293
+ "var(--color-brand-text-fg)",
294
+ "var(--color-brand-text-bg)",
295
+ "text-fg on text-bg"
296
+ )}
297
+ ${makeDemoPair(
298
+ "var(--color-brand-text-accent)",
299
+ "var(--color-brand-text-bg)",
300
+ "text-accent on text-bg"
301
+ )}
302
+ ${makeDemoPair(
303
+ "",
304
+ "var(--color-brand-accent)",
305
+ "accent",
306
+ "single"
307
+ )}
308
+ ${makeDemoPair(
309
+ "",
310
+ "var(--color-brand-accent2)",
311
+ "accent2",
312
+ "single"
313
+ )}
314
+ ${makeDemoPair(
315
+ "var(--color-brand-text-on-accent)",
316
+ "var(--color-brand-accent)",
317
+ "text-on-accent on accent"
318
+ )}
319
+ ${makeDemoPair(
320
+ "var(--color-brand-text-on-accent2)",
321
+ "var(--color-brand-accent2)",
322
+ "text-on-accent2 on accent2"
323
+ )}
324
+ </div>
325
+ </div>
326
+ </div>
207
327
  </body>
208
328
  <script>
209
- document.addEventListener("click", (event) => {
210
- navigator.clipboard.writeText(
211
- \`var(--color-\${event.target.getAttribute(
212
- "data-color-group-name"
213
- )}-\${event.target.getAttribute(
214
- "data-color-name"
215
- )}-\${event.target.getAttribute(
216
- "data-color-shade-number"
217
- )})\`
218
- );
329
+ function pop_toast() {
219
330
  const toast = document.createElement("div");
220
331
  document.body.appendChild(toast);
221
332
  toast.classList.add("toast");
@@ -234,8 +345,62 @@ class ColorsBuilder extends Builder {
234
345
  toast.addEventListener("animationend", () => {
235
346
  toast.remove();
236
347
  });
348
+ }
349
+
350
+ document.addEventListener("click", (event) => {
351
+ const box = event.target.closest(".color-box");
352
+ if (box) {
353
+ const css_var = box.classList.contains(
354
+ "is-main"
355
+ )
356
+ ? \`var(--color-\${box.getAttribute(
357
+ "data-color-group-name"
358
+ )}-\${box.getAttribute(
359
+ "data-color-name"
360
+ )})\`
361
+ : \`var(--color-\${box.getAttribute(
362
+ "data-color-group-name"
363
+ )}-\${box.getAttribute(
364
+ "data-color-name"
365
+ )}-\${box.getAttribute(
366
+ "data-color-shade-number"
367
+ )})\`;
368
+ navigator.clipboard.writeText(css_var);
369
+ pop_toast();
370
+ }
371
+ const pair = event.target.closest(".pair");
372
+ if (pair) {
373
+ let to_copy = "";
374
+ if (pair.classList.contains("single")) {
375
+ to_copy = pair.getAttribute("data-bg");
376
+ } else {
377
+ to_copy = \`background-color: \${pair.getAttribute(
378
+ "data-bg"
379
+ )};\\ncolor: \${pair.getAttribute(
380
+ "data-fg"
381
+ )};\`;
382
+ }
383
+ navigator.clipboard.writeText(to_copy);
384
+ pop_toast();
385
+ }
237
386
  });
238
387
  </script>
388
+ <script>
389
+ (async function () {
390
+ const response = await (
391
+ await fetch("/dist/notifier.json")
392
+ ).json();
393
+ const ws = new WebSocket(
394
+ \`http://localhost:\${response.port}\`
395
+ );
396
+ ws.addEventListener("message", (event) => {
397
+ console.log(event);
398
+ if (event.data == "${APP_BACK_ALIVE_SIGNAL}") {
399
+ document.location = document.location; // refresh
400
+ }
401
+ });
402
+ })();
403
+ </script>
239
404
  </html>`,
240
405
  "html"
241
406
  );
package/lib/notifier.js CHANGED
@@ -1,5 +1,72 @@
1
1
  import { WebSocketServer } from "ws";
2
- function make_notifier(port) {
2
+ const APP_DOWN_ERROR_MESSAGE = "App is currently down";
3
+ const APP_BACK_ALIVE_SIGNAL = "app-back-alive";
4
+ const sleep = (time) => new Promise((resolve) => {
5
+ setTimeout(resolve, time);
6
+ });
7
+ async function get_status(app_port) {
8
+ const r = await fetch(`http://127.0.0.1:${app_port}/status.json`);
9
+ return await r.json();
10
+ }
11
+ async function wait_for_run_id_to_change(app_port) {
12
+ let first_timestamp;
13
+ try {
14
+ const { started_at } = await get_status(app_port);
15
+ first_timestamp = started_at;
16
+ } catch (e) {
17
+ await wait_for_app_to_be_stable(app_port);
18
+ return;
19
+ }
20
+ if (!first_timestamp) {
21
+ throw new Error(APP_DOWN_ERROR_MESSAGE);
22
+ }
23
+ while (true) {
24
+ const { started_at } = await get_status(app_port).catch(() => ({
25
+ started_at: first_timestamp
26
+ }));
27
+ if (started_at !== first_timestamp) {
28
+ return;
29
+ }
30
+ await sleep(100);
31
+ }
32
+ }
33
+ async function wait_for_app_to_be_stable(app_port, n = 3) {
34
+ console.log("Waiting for app to be stable....");
35
+ let counter = 0;
36
+ while (true) {
37
+ console.debug("notifier.ts:53");
38
+ const { status } = await get_status(app_port).catch(() => ({
39
+ status: "down"
40
+ }));
41
+ if (status == "running") {
42
+ console.log(counter);
43
+ counter++;
44
+ } else {
45
+ counter = 0;
46
+ }
47
+ if (counter == n) {
48
+ return;
49
+ }
50
+ await sleep(100);
51
+ }
52
+ }
53
+ let restart_promise = null;
54
+ async function wait_for_app_restart(app_port) {
55
+ if (restart_promise) {
56
+ return restart_promise;
57
+ }
58
+ try {
59
+ restart_promise = wait_for_run_id_to_change(app_port);
60
+ await restart_promise;
61
+ } catch (e) {
62
+ if (e.message !== APP_DOWN_ERROR_MESSAGE) {
63
+ throw e;
64
+ }
65
+ }
66
+ restart_promise = null;
67
+ await wait_for_app_to_be_stable(app_port);
68
+ }
69
+ function make_notifier(port, app_port) {
3
70
  const server = new WebSocketServer({
4
71
  port
5
72
  });
@@ -15,9 +82,13 @@ function make_notifier(port) {
15
82
  );
16
83
  return function notify(message) {
17
84
  sockets.forEach((s) => s.send(message));
85
+ void wait_for_app_restart(app_port).then(() => {
86
+ sockets.forEach((s) => s.send(APP_BACK_ALIVE_SIGNAL));
87
+ });
18
88
  };
19
89
  }
20
90
  export {
91
+ APP_BACK_ALIVE_SIGNAL,
21
92
  make_notifier
22
93
  };
23
94
  //# sourceMappingURL=notifier.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sealcode/sealgen",
3
- "version": "0.17.14",
3
+ "version": "0.17.15",
4
4
  "description": "Module to automate adding routes and collections to a sealious application",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
package/src/build.ts CHANGED
@@ -96,7 +96,7 @@ async function build(watch: boolean): Promise<void> {
96
96
  if (watch) {
97
97
  const watcher = chokidar.watch("src", { ignoreInitial: true });
98
98
  const port = await get_notifier_port();
99
- const notifier = make_notifier(port);
99
+ const notifier = make_notifier(port, 8080); // TODO: parse the app config to get the actual port
100
100
  await write_notifier_config(watch, port);
101
101
  watcher.on("all", (_, file_path) => {
102
102
  console.log("Detected a change!", file_path);
@@ -10,9 +10,11 @@ import { formatWithPrettier } from "../utils/prettier.js";
10
10
  import { Builder } from "./builder.js";
11
11
  import { exec } from "../utils/exec.js";
12
12
 
13
+ import { APP_BACK_ALIVE_SIGNAL } from "../notifier.js";
14
+
13
15
  export const COLORS_TS_PATH = "src/back/colors.ts"; // keeping it in src/back instead of just src to make typescript hints work there
14
16
  export const COLORS_CSS_PATH = "src/colors.css";
15
- export const COLORS_HTML_PATH = "src/colors.html";
17
+ export const COLORS_HTML_PATH = "public/dist/colors.html";
16
18
 
17
19
  export class ColorsBuilder extends Builder {
18
20
  getName(): string {
@@ -88,15 +90,18 @@ export class ColorsBuilder extends Builder {
88
90
  group_name: string;
89
91
  is_main?: boolean;
90
92
  }) {
93
+ const c = colord(color);
91
94
  return /* HTML */ `<div
92
- class="color-box ${colord(color).isDark()
93
- ? "is-dark"
94
- : ""} ${is_main ? "is-main" : ""}"
95
+ class="color-box ${c.isDark() ? "is-dark" : ""} ${is_main
96
+ ? "is-main"
97
+ : ""} ${c.toHsl().l > 90 ? "is-almost-white" : ""}"
95
98
  style="background-color: ${color}; order: ${parseInt(shade_no)}"
96
99
  data-color-name="${name}"
97
100
  data-color-group-name="${group_name}"
98
101
  data-color-shade-number="${shade_no}"
99
- ></div>`;
102
+ >
103
+ <span class="hex">${c.toHex()}</span>
104
+ </div>`;
100
105
  }
101
106
 
102
107
  for (const group_name of Object.keys(color_groups).sort((key) =>
@@ -150,10 +155,30 @@ export class ColorsBuilder extends Builder {
150
155
  "css"
151
156
  );
152
157
 
158
+ function makeDemoPair(
159
+ fg: string,
160
+ bg: string,
161
+ text: string,
162
+ classname = ""
163
+ ) {
164
+ return /* HTML */ ` <div
165
+ class="pair ${classname}"
166
+ data-fg="${fg}"
167
+ data-bg="${bg}"
168
+ style="background-color: ${bg}; color: ${fg}"
169
+ >
170
+ <span>${text}</span>
171
+ </div>`;
172
+ }
173
+
153
174
  const html = await formatWithPrettier(
154
175
  /* HTML */ `<!DOCTYPE html>
155
176
  <html>
156
177
  <head>
178
+ <title>Color palettes</title>
179
+ <style>
180
+ ${css}
181
+ </style>
157
182
  <style>
158
183
  * {
159
184
  font-family: sans-serif;
@@ -174,6 +199,10 @@ export class ColorsBuilder extends Builder {
174
199
  &:hover {
175
200
  transform: scale(1.1);
176
201
  z-index: 2;
202
+
203
+ .hex {
204
+ visibility: visible;
205
+ }
177
206
  }
178
207
 
179
208
  &.is-main {
@@ -182,22 +211,42 @@ export class ColorsBuilder extends Builder {
182
211
  margin-top: -8px;
183
212
  box-shadow: 0px 0px 6px 2px white;
184
213
  z-index: 1;
214
+
215
+ &:before {
216
+ font-weight: bold;
217
+ }
218
+ }
219
+
220
+ &.is-almost-white.is-main {
221
+ box-shadow: 0px 0px 6px 1px #00000078;
222
+ }
223
+
224
+ .hex {
225
+ padding: 8px;
226
+ font-family: Menlo, Consolas, Monaco,
227
+ Liberation Mono, Lucida Console,
228
+ monospace;
229
+ opacity: 0.5;
230
+ visibility: hidden;
231
+ }
232
+
233
+ &.is-dark .hex {
234
+ color: white;
185
235
  }
186
236
  }
187
237
 
188
- .color-box:after {
238
+ .color-box:before {
189
239
  content: attr(data-color-shade-number);
190
240
  box-sizing: border-box;
191
241
  display: block;
192
242
  color: black;
193
243
  font-size: 14px;
194
244
  width: 100%;
195
- height: 100%;
196
245
  padding: 10px;
197
246
  opacity: 0.3;
198
247
  }
199
248
 
200
- .color-box.is-dark:after {
249
+ .color-box.is-dark:before {
201
250
  color: white;
202
251
  }
203
252
 
@@ -223,22 +272,88 @@ export class ColorsBuilder extends Builder {
223
272
  animation-timing-function: ease-out;
224
273
  z-index: 3;
225
274
  }
275
+
276
+ .container {
277
+ width: 100vw;
278
+ display: flex;
279
+ flex-flow: row wrap;
280
+ gap: 16px;
281
+
282
+ @media (max-width: 1400px) {
283
+ .demo {
284
+ order: 1;
285
+ }
286
+ .pallete {
287
+ order: 2;
288
+ }
289
+ }
290
+
291
+ .canvas {
292
+ background-color: var(--color-brand-canvas);
293
+ display: flex;
294
+ flex-flow: column;
295
+ border: 1px dashed gray;
296
+ color: gray;
297
+
298
+ gap: 8px;
299
+ padding: 8px;
300
+
301
+ & > * {
302
+ padding: 8px;
303
+ color: black;
304
+ }
305
+
306
+ .pair.single span {
307
+ opacity: 0.5;
308
+ }
309
+ }
310
+ }
226
311
  </style>
227
312
  </head>
228
313
  <body>
229
- ${html_colors}
314
+ <div class="container">
315
+ <div class="pallete">${html_colors}</div>
316
+ <div class="demo">
317
+ <div class="canvas">
318
+ <span>canvas</span>
319
+ ${makeDemoPair(
320
+ "var(--color-brand-text-fg)",
321
+ "var(--color-brand-text-bg)",
322
+ "text-fg on text-bg"
323
+ )}
324
+ ${makeDemoPair(
325
+ "var(--color-brand-text-accent)",
326
+ "var(--color-brand-text-bg)",
327
+ "text-accent on text-bg"
328
+ )}
329
+ ${makeDemoPair(
330
+ "",
331
+ "var(--color-brand-accent)",
332
+ "accent",
333
+ "single"
334
+ )}
335
+ ${makeDemoPair(
336
+ "",
337
+ "var(--color-brand-accent2)",
338
+ "accent2",
339
+ "single"
340
+ )}
341
+ ${makeDemoPair(
342
+ "var(--color-brand-text-on-accent)",
343
+ "var(--color-brand-accent)",
344
+ "text-on-accent on accent"
345
+ )}
346
+ ${makeDemoPair(
347
+ "var(--color-brand-text-on-accent2)",
348
+ "var(--color-brand-accent2)",
349
+ "text-on-accent2 on accent2"
350
+ )}
351
+ </div>
352
+ </div>
353
+ </div>
230
354
  </body>
231
355
  <script>
232
- document.addEventListener("click", (event) => {
233
- navigator.clipboard.writeText(
234
- \`var(--color-\${event.target.getAttribute(
235
- "data-color-group-name"
236
- )}-\${event.target.getAttribute(
237
- "data-color-name"
238
- )}-\${event.target.getAttribute(
239
- "data-color-shade-number"
240
- )})\`
241
- );
356
+ function pop_toast() {
242
357
  const toast = document.createElement("div");
243
358
  document.body.appendChild(toast);
244
359
  toast.classList.add("toast");
@@ -257,8 +372,62 @@ export class ColorsBuilder extends Builder {
257
372
  toast.addEventListener("animationend", () => {
258
373
  toast.remove();
259
374
  });
375
+ }
376
+
377
+ document.addEventListener("click", (event) => {
378
+ const box = event.target.closest(".color-box");
379
+ if (box) {
380
+ const css_var = box.classList.contains(
381
+ "is-main"
382
+ )
383
+ ? \`var(--color-\${box.getAttribute(
384
+ "data-color-group-name"
385
+ )}-\${box.getAttribute(
386
+ "data-color-name"
387
+ )})\`
388
+ : \`var(--color-\${box.getAttribute(
389
+ "data-color-group-name"
390
+ )}-\${box.getAttribute(
391
+ "data-color-name"
392
+ )}-\${box.getAttribute(
393
+ "data-color-shade-number"
394
+ )})\`;
395
+ navigator.clipboard.writeText(css_var);
396
+ pop_toast();
397
+ }
398
+ const pair = event.target.closest(".pair");
399
+ if (pair) {
400
+ let to_copy = "";
401
+ if (pair.classList.contains("single")) {
402
+ to_copy = pair.getAttribute("data-bg");
403
+ } else {
404
+ to_copy = \`background-color: \${pair.getAttribute(
405
+ "data-bg"
406
+ )};\\ncolor: \${pair.getAttribute(
407
+ "data-fg"
408
+ )};\`;
409
+ }
410
+ navigator.clipboard.writeText(to_copy);
411
+ pop_toast();
412
+ }
260
413
  });
261
414
  </script>
415
+ <script>
416
+ (async function () {
417
+ const response = await (
418
+ await fetch("/dist/notifier.json")
419
+ ).json();
420
+ const ws = new WebSocket(
421
+ \`http://localhost:\${response.port}\`
422
+ );
423
+ ws.addEventListener("message", (event) => {
424
+ console.log(event);
425
+ if (event.data == "${APP_BACK_ALIVE_SIGNAL}") {
426
+ document.location = document.location; // refresh
427
+ }
428
+ });
429
+ })();
430
+ </script>
262
431
  </html>`,
263
432
  "html"
264
433
  );
package/src/notifier.ts CHANGED
@@ -1,6 +1,95 @@
1
1
  import { WebSocketServer, WebSocket } from "ws";
2
2
 
3
- export function make_notifier(port: number) {
3
+ const APP_DOWN_ERROR_MESSAGE = "App is currently down";
4
+ export const APP_BACK_ALIVE_SIGNAL = "app-back-alive";
5
+
6
+ const sleep = (time: number) =>
7
+ new Promise((resolve) => {
8
+ setTimeout(resolve, time);
9
+ });
10
+
11
+ async function get_status(
12
+ app_port: number
13
+ ): Promise<{ started_at: number; status: string }> {
14
+ const r = await fetch(`http://127.0.0.1:${app_port}/status.json`);
15
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
16
+ return (await r.json()) as { started_at: number; status: string };
17
+ }
18
+
19
+ async function wait_for_run_id_to_change(app_port: number) {
20
+ let first_timestamp: number;
21
+ try {
22
+ const { started_at } = await get_status(app_port);
23
+ first_timestamp = started_at;
24
+ } catch (e) {
25
+ await wait_for_app_to_be_stable(app_port);
26
+ return;
27
+ }
28
+
29
+ if (!first_timestamp) {
30
+ throw new Error(APP_DOWN_ERROR_MESSAGE);
31
+ }
32
+
33
+ // eslint-disable-next-line no-constant-condition
34
+ while (true) {
35
+ // eslint-disable-next-line no-await-in-loop
36
+ const { started_at } = await get_status(app_port).catch(() => ({
37
+ started_at: first_timestamp,
38
+ }));
39
+ if (started_at !== first_timestamp) {
40
+ return;
41
+ }
42
+ // eslint-disable-next-line no-await-in-loop
43
+ await sleep(100);
44
+ }
45
+ }
46
+
47
+ async function wait_for_app_to_be_stable(app_port: number, n = 3) {
48
+ // eslint-disable-next-line no-console
49
+ console.log("Waiting for app to be stable....");
50
+ let counter = 0;
51
+ // eslint-disable-next-line no-constant-condition
52
+ while (true) {
53
+ console.debug("notifier.ts:53");
54
+ // eslint-disable-next-line no-await-in-loop
55
+ const { status } = await get_status(app_port).catch(() => ({
56
+ status: "down",
57
+ }));
58
+ if (status == "running") {
59
+ // eslint-disable-next-line no-console
60
+ console.log(counter);
61
+ counter++;
62
+ } else {
63
+ counter = 0;
64
+ }
65
+ if (counter == n) {
66
+ return;
67
+ }
68
+ // eslint-disable-next-line no-await-in-loop
69
+ await sleep(100);
70
+ }
71
+ }
72
+
73
+ let restart_promise: Promise<void> | null = null;
74
+
75
+ async function wait_for_app_restart(app_port: number) {
76
+ if (restart_promise) {
77
+ return restart_promise;
78
+ }
79
+ try {
80
+ restart_promise = wait_for_run_id_to_change(app_port);
81
+ await restart_promise;
82
+ } catch (e) {
83
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
84
+ if (e.message !== APP_DOWN_ERROR_MESSAGE) {
85
+ throw e;
86
+ }
87
+ }
88
+ restart_promise = null;
89
+ await wait_for_app_to_be_stable(app_port);
90
+ }
91
+
92
+ export function make_notifier(port: number, app_port: number) {
4
93
  const server = new WebSocketServer({
5
94
  port,
6
95
  });
@@ -20,5 +109,8 @@ export function make_notifier(port: number) {
20
109
  );
21
110
  return function notify(message: string) {
22
111
  sockets.forEach((s) => s.send(message));
112
+ void wait_for_app_restart(app_port).then(() => {
113
+ sockets.forEach((s) => s.send(APP_BACK_ALIVE_SIGNAL));
114
+ });
23
115
  };
24
116
  }