castle-web-sdk 0.4.2 → 0.4.4

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/dist/castle.d.ts CHANGED
@@ -2,7 +2,7 @@ export { isEdit } from "./context";
2
2
  export { CastleError } from "./errors";
3
3
  export { Leaderboard } from "./leaderboard";
4
4
  export type { LeaderboardData, LeaderboardEntry, LeaderboardOptions, LeaderboardScope, LeaderboardSort, } from "./leaderboard";
5
- export { CARD_RATIO, initCard, setup, writeFile } from "./runtime";
5
+ export { CARD_RATIO, initCard, onBeforeRestart, setup, writeFile } from "./runtime";
6
6
  export { SharedStorage, Storage } from "./storage";
7
7
  export { Time } from "./time";
8
8
  export type { CastleClockZone, CastleDateParts, CastleTimeApi } from "./time";
package/dist/castle.js CHANGED
@@ -2,7 +2,7 @@
2
2
  export { isEdit } from "./context";
3
3
  export { CastleError } from "./errors";
4
4
  export { Leaderboard } from "./leaderboard";
5
- export { CARD_RATIO, initCard, setup, writeFile } from "./runtime";
5
+ export { CARD_RATIO, initCard, onBeforeRestart, setup, writeFile } from "./runtime";
6
6
  export { SharedStorage, Storage } from "./storage";
7
7
  export { Time } from "./time";
8
8
  export { User } from "./user";
package/dist/runtime.d.ts CHANGED
@@ -9,4 +9,5 @@ interface LocalResponse {
9
9
  export declare function setup(): void;
10
10
  export declare function writeFile(path: string, contents: string): Promise<LocalResponse>;
11
11
  export declare function initCard(): HTMLDivElement;
12
+ export declare function onBeforeRestart(hook: () => void | Promise<void>): () => void;
12
13
  export {};
package/dist/runtime.js CHANGED
@@ -275,12 +275,38 @@ function handleLocalMessage(msg) {
275
275
  });
276
276
  }
277
277
  else if (msg.type === "restart") {
278
- location.reload();
278
+ scheduleRestart();
279
279
  }
280
280
  else if (msg.type === "write_file_response") {
281
281
  resolveLocalRequest(msg);
282
282
  }
283
283
  }
284
+ // Restart (from `castle-web restart` / task agents) is debounced so a burst
285
+ // of reload requests -- several tasks finishing close together -- produces
286
+ // one reload. Before reloading, registered hooks run (the kit editor flushes
287
+ // its debounced unsaved edits there) so in-flight work isn't lost.
288
+ const RESTART_DEBOUNCE_MS = 1500;
289
+ let restartTimer = null;
290
+ const beforeRestartHooks = new Set();
291
+ export function onBeforeRestart(hook) {
292
+ beforeRestartHooks.add(hook);
293
+ return () => beforeRestartHooks.delete(hook);
294
+ }
295
+ function scheduleRestart() {
296
+ if (restartTimer !== null)
297
+ clearTimeout(restartTimer);
298
+ restartTimer = setTimeout(() => {
299
+ void (async () => {
300
+ try {
301
+ await Promise.all([...beforeRestartHooks].map((hook) => hook()));
302
+ }
303
+ catch {
304
+ // a failed flush shouldn't block the reload
305
+ }
306
+ location.reload();
307
+ })();
308
+ }, RESTART_DEBOUNCE_MS);
309
+ }
284
310
  function localWsUrl(path) {
285
311
  const url = new URL(path, location.href);
286
312
  url.protocol = location.protocol === "https:" ? "wss:" : "ws:";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "castle-web-sdk",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "type": "module",
5
5
  "main": "dist/castle.js",
6
6
  "types": "dist/castle.d.ts",
package/src/castle.ts CHANGED
@@ -10,7 +10,7 @@ export type {
10
10
  LeaderboardScope,
11
11
  LeaderboardSort,
12
12
  } from "./leaderboard";
13
- export { CARD_RATIO, initCard, setup, writeFile } from "./runtime";
13
+ export { CARD_RATIO, initCard, onBeforeRestart, setup, writeFile } from "./runtime";
14
14
  export { SharedStorage, Storage } from "./storage";
15
15
  export { Time } from "./time";
16
16
  export type { CastleClockZone, CastleDateParts, CastleTimeApi } from "./time";
package/src/runtime.ts CHANGED
@@ -332,12 +332,39 @@ function handleLocalMessage(msg: IncomingMessage): void {
332
332
  });
333
333
  });
334
334
  } else if (msg.type === "restart") {
335
- location.reload();
335
+ scheduleRestart();
336
336
  } else if (msg.type === "write_file_response") {
337
337
  resolveLocalRequest(msg);
338
338
  }
339
339
  }
340
340
 
341
+ // Restart (from `castle-web restart` / task agents) is debounced so a burst
342
+ // of reload requests -- several tasks finishing close together -- produces
343
+ // one reload. Before reloading, registered hooks run (the kit editor flushes
344
+ // its debounced unsaved edits there) so in-flight work isn't lost.
345
+ const RESTART_DEBOUNCE_MS = 1500;
346
+ let restartTimer: ReturnType<typeof setTimeout> | null = null;
347
+ const beforeRestartHooks = new Set<() => void | Promise<void>>();
348
+
349
+ export function onBeforeRestart(hook: () => void | Promise<void>): () => void {
350
+ beforeRestartHooks.add(hook);
351
+ return () => beforeRestartHooks.delete(hook);
352
+ }
353
+
354
+ function scheduleRestart(): void {
355
+ if (restartTimer !== null) clearTimeout(restartTimer);
356
+ restartTimer = setTimeout(() => {
357
+ void (async () => {
358
+ try {
359
+ await Promise.all([...beforeRestartHooks].map((hook) => hook()));
360
+ } catch {
361
+ // a failed flush shouldn't block the reload
362
+ }
363
+ location.reload();
364
+ })();
365
+ }, RESTART_DEBOUNCE_MS);
366
+ }
367
+
341
368
  function localWsUrl(path: string): string {
342
369
  const url = new URL(path, location.href);
343
370
  url.protocol = location.protocol === "https:" ? "wss:" : "ws:";