@effing/create 0.6.1 → 0.7.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effing/create",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "description": "Create a new Effing project",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
@@ -42,7 +42,7 @@ const RENDER_SCALES = [
42
42
 
43
43
  type RenderSuccess = {
44
44
  renderSuccess: true;
45
- videoUrl: string;
45
+ progressUrl: string;
46
46
  renderScale: number;
47
47
  };
48
48
  type RenderError = {
@@ -113,7 +113,7 @@ export async function loader({ request, params }: Route.LoaderArgs) {
113
113
 
114
114
  if (warmupResponse.ok) {
115
115
  const warmupData = await warmupResponse.json();
116
- warmupStreamUrl = warmupData.url;
116
+ warmupStreamUrl = warmupData.progressUrl;
117
117
  }
118
118
  } catch {
119
119
  // Warmup is best-effort, don't fail the page load
@@ -212,8 +212,8 @@ async function handleRender(
212
212
  }
213
213
  }
214
214
 
215
- const { url } = await createResponse.json();
216
- return { renderSuccess: true, renderScale: scale, videoUrl: url };
215
+ const { progressUrl } = await createResponse.json();
216
+ return { renderSuccess: true, renderScale: scale, progressUrl };
217
217
  } catch (err) {
218
218
  return {
219
219
  renderSuccess: false,
@@ -274,15 +274,34 @@ export default function EffiePreviewPage() {
274
274
  return () => clearInterval(interval);
275
275
  }, [render.startTime, render.playbackTime]);
276
276
 
277
- // Update render state when action completes successfully
277
+ // Connect to SSE progress when render action completes
278
278
  useEffect(() => {
279
- if (actionData && isRenderSuccess(actionData)) {
280
- setRender((prev) => ({
281
- ...prev,
282
- videoUrl: actionData.videoUrl,
283
- scale: actionData.renderScale,
284
- }));
285
- }
279
+ if (!actionData || !isRenderSuccess(actionData)) return;
280
+
281
+ const { progressUrl, renderScale } = actionData;
282
+ const eventSource = new EventSource(progressUrl);
283
+
284
+ eventSource.addEventListener("ready", (e) => {
285
+ try {
286
+ const { videoUrl } = JSON.parse(e.data);
287
+ setRender((prev) => ({
288
+ ...prev,
289
+ videoUrl,
290
+ scale: renderScale,
291
+ }));
292
+ } catch {
293
+ // Ignore parse errors
294
+ }
295
+ eventSource.close();
296
+ });
297
+
298
+ eventSource.addEventListener("error", () => {
299
+ eventSource.close();
300
+ });
301
+
302
+ return () => {
303
+ eventSource.close();
304
+ };
286
305
  }, [actionData]);
287
306
 
288
307
  const warmup = useEffieWarmup(warmupStreamUrl);
@@ -11,14 +11,14 @@
11
11
  "typecheck": "react-router typegen && tsc"
12
12
  },
13
13
  "dependencies": {
14
- "@effing/annie": "^0.6.1",
15
- "@effing/annie-player": "^0.6.1",
16
- "@effing/effie": "^0.6.1",
17
- "@effing/effie-preview": "^0.6.1",
18
- "@effing/ffs": "^0.6.1",
19
- "@effing/satori": "^0.6.1",
20
- "@effing/serde": "^0.6.1",
21
- "@effing/tween": "^0.6.1",
14
+ "@effing/annie": "^0.7.1",
15
+ "@effing/annie-player": "^0.7.1",
16
+ "@effing/effie": "^0.7.1",
17
+ "@effing/effie-preview": "^0.7.1",
18
+ "@effing/ffs": "^0.7.1",
19
+ "@effing/satori": "^0.7.1",
20
+ "@effing/serde": "^0.7.1",
21
+ "@effing/tween": "^0.7.1",
22
22
  "@react-router/node": "^7.0.0",
23
23
  "@react-router/serve": "^7.0.0",
24
24
  "cross-env": "^7.0.3",
@@ -1,16 +1,20 @@
1
1
  /* global clients */
2
2
 
3
3
  // FFS render URLs (GET /render/{uuid}) are one-time-consumption — the server
4
- // deletes the job after streaming the response. However, Chrome makes follow-up
5
- // requests to the same URL (e.g. to re-read the moov atom for seeking), which
6
- // return 404 and cause the video to hang at larger scales. This service worker
7
- // caches the response so those subsequent requests are served from cache.
4
+ // deletes the job after streaming the response. However, browsers might make
5
+ // follow-up requests to the same URL (e.g. to re-read the moov atom for seeking),
6
+ // which return 404 and cause the video to break. This service worker caches the
7
+ // response so those subsequent requests are served from cache.
8
8
 
9
9
  const CACHE_NAME = "ffs-render-cache";
10
- const RENDER_URL_PATTERN = /\/render\/[0-9a-f-]{36}$/;
10
+ const RENDER_URL_PATTERN = /\/render\/[0-9a-f-]{36}\/video$/;
11
11
  const MAX_ENTRIES = 5;
12
12
 
13
+ /** @type {string[]} URLs in insertion order — reset on activate alongside the cache. */
14
+ const cacheOrder = [];
15
+
13
16
  self.addEventListener("activate", (event) => {
17
+ cacheOrder.length = 0;
14
18
  event.waitUntil(caches.delete(CACHE_NAME).then(() => clients.claim()));
15
19
  });
16
20
 
@@ -25,14 +29,16 @@ self.addEventListener("fetch", (event) => {
25
29
  const response = await fetch(event.request);
26
30
  if (response.ok) {
27
31
  await cache.put(event.request, response.clone());
32
+ cacheOrder.push(event.request.url);
28
33
 
29
- // Prune oldest entries to keep the cache bounded.
30
- const keys = await cache.keys();
31
- if (keys.length > MAX_ENTRIES) {
32
- await Promise.all(
33
- keys
34
- .slice(0, keys.length - MAX_ENTRIES)
35
- .map((k) => cache.delete(k)),
34
+ // Prune oldest entries in the background — don't block the response.
35
+ if (cacheOrder.length > MAX_ENTRIES) {
36
+ const toDelete = cacheOrder.splice(
37
+ 0,
38
+ cacheOrder.length - MAX_ENTRIES,
39
+ );
40
+ event.waitUntil(
41
+ Promise.all(toDelete.map((url) => cache.delete(url))),
36
42
  );
37
43
  }
38
44
  }