collective

Browse / github.com/storytold/artcraft-services / patch

fix: stop testing promises in conditionals (SonarCloud S6544)

fix: stop testing promises in conditionals (SonarCloud S6544) Resolves the 20 `typescript:S6544` findings. - **Cached promises** (`create-3d-modal`, `DndAsset`, `ammoLoader`, `google-fonts`, `audio-manager`, `waveform-cache`, `video-cache`, `image-node`, `sticker-node`, `hero-wordmark`, `heading-flow`): `if (promise)` is always true, so each now narrows by identity (`=== undefined` / `!== null`). Same behaviour, no always-true branch. - **`SceneManagerAPI`**: the interface declared `void` while `SceneManager` returned promises for `create`/`retrieve`/`update`/`delete`; the interface now matches the implementation. - **`scene.ts`**: both CORS-less loader

Status
open
Score
0.00
Adopters
0 other operators
Author
dylanmtaylor (imported from pull request #181, open)
Base
e57ee3d63ac478a24149384bf1473ef97aa067f8
Size
+75 −69 in 14 files
Review
pending
Shared
2026-10-28
Id
8384d6a06ea959a643fcef0e

How it went

No other operator has reported using it yet.

Diff

raw · frontend/apps/artcraft-website-next/src/components/landing/hero-wordmark.tsx frontend/apps/artcraft-website-next/src/components/ruler/heading-flow.tsx frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx frontend/libs/components/pagescene/src/lib/DragAndDrop/DndAsset.ts frontend/libs/components/pagescene/src/lib/engine/ammoLoader.ts frontend/libs/components/pagescene/src/lib/engine/scene.ts frontend/libs/components/pagescene/src/lib/engine/scene_manager_api.ts frontend/libs/components/upload-modal/src/lib/UploadModal3D/utilities/convertFbxToGlb.worker.ts frontend/libs/components/video-editor/src/lib/core/managers/audio-manager.ts frontend/libs/components/video-editor/src/lib/fonts/google-fonts.ts frontend/libs/components/video-editor/src/lib/services/renderer/nodes/image-node.ts frontend/libs/components/video-editor/src/lib/services/renderer/nodes/sticker-node.ts frontend/libs/components/video-editor/src/lib/services/video-cache/service.ts frontend/libs/components/video-editor/src/lib/services/waveform-cache/service.ts

diff --git a/frontend/apps/artcraft-website-next/src/components/landing/hero-wordmark.tsx b/frontend/apps/artcraft-website-next/src/components/landing/hero-wordmark.tsx--- a/frontend/apps/artcraft-website-next/src/components/landing/hero-wordmark.tsx+++ b/frontend/apps/artcraft-website-next/src/components/landing/hero-wordmark.tsx@@ -166,7 +166,7 @@ export default function HeroMasthead() {     };      const ready = document.fonts?.ready;-    if (ready) ready.then(compute).catch(compute);+    if (ready !== undefined) ready.then(compute).catch(compute);     else compute();     const ro = new ResizeObserver(compute);     ro.observe(box);diff --git a/frontend/apps/artcraft-website-next/src/components/ruler/heading-flow.tsx b/frontend/apps/artcraft-website-next/src/components/ruler/heading-flow.tsx--- a/frontend/apps/artcraft-website-next/src/components/ruler/heading-flow.tsx+++ b/frontend/apps/artcraft-website-next/src/components/ruler/heading-flow.tsx@@ -167,7 +167,7 @@ export default function HeadingFlow({       setMetrics(measureLabels(sections.map((s) => s.label)));     };     const ready = document.fonts?.ready;-    if (ready) ready.then(run).catch(run);+    if (ready !== undefined) ready.then(run).catch(run);     else run();     return () => {       cancelled = true;diff --git a/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx b/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx--- a/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx+++ b/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx@@ -38,7 +38,7 @@ export const Create3dModal = ({ onModelComplete }: Create3dModalProps = {}) => {   // Listen for generation complete events   useEffect(() => {     let isUnmounted = false;-    let unlisten: Promise<UnlistenFn>;+    let unlisten: Promise<UnlistenFn> | undefined;      const setup = async () => {       unlisten = listen("generation-complete-event", (event: any) => {@@ -102,7 +102,7 @@ export const Create3dModal = ({ onModelComplete }: Create3dModalProps = {}) => {      return () => {       isUnmounted = true;-      if (unlisten) {+      if (unlisten !== undefined) {         unlisten.then((f) => f());       }     };diff --git a/frontend/libs/components/pagescene/src/lib/DragAndDrop/DndAsset.ts b/frontend/libs/components/pagescene/src/lib/DragAndDrop/DndAsset.ts--- a/frontend/libs/components/pagescene/src/lib/DragAndDrop/DndAsset.ts+++ b/frontend/libs/components/pagescene/src/lib/DragAndDrop/DndAsset.ts@@ -273,7 +273,7 @@ class DndAsset {       return;     }     const inspection = this.clipNodeNamesCache.get(item.media_id);-    if (!inspection) {+    if (inspection === undefined) {       // No inspection in flight (shouldn't happen — pointer-down starts       // one): add unvalidated, matching the ungated click-to-add path.       this.completeAnimationDrop(editor, item, target, undefined);diff --git a/frontend/libs/components/pagescene/src/lib/engine/ammoLoader.ts b/frontend/libs/components/pagescene/src/lib/engine/ammoLoader.ts--- a/frontend/libs/components/pagescene/src/lib/engine/ammoLoader.ts+++ b/frontend/libs/components/pagescene/src/lib/engine/ammoLoader.ts@@ -8,7 +8,7 @@ let ammoLoadPromise: Promise<void> | null = null;  export const ensureAmmoLoaded = (): Promise<void> => {-  if (ammoLoadPromise) return ammoLoadPromise;+  if (ammoLoadPromise !== null) return ammoLoadPromise;    ammoLoadPromise = new Promise<void>((resolve, reject) => {     if (typeof document === "undefined") {diff --git a/frontend/libs/components/pagescene/src/lib/engine/scene.ts b/frontend/libs/components/pagescene/src/lib/engine/scene.ts--- a/frontend/libs/components/pagescene/src/lib/engine/scene.ts+++ b/frontend/libs/components/pagescene/src/lib/engine/scene.ts@@ -1026,22 +1026,21 @@ class Scene {     onComplete: () => void,     signal?: AbortSignal,   ): Promise<GLTF> {-    return new Promise<GLTF>(async (resolve, reject) => {-      let buffer;-      try {-        // Host-supplied CORS-bypassed binary fetch (FetchProxy under-        // Tauri; plain fetch under web hosts that allow it).-        buffer = await (-          await this.deps.fetchAsset(media_url, { signal })-        ).arrayBuffer();-      } catch (error) {-        if ((error as { name?: string })?.name !== "AbortError") {-          console.error("load GLB from Tauri error:", error);-        }-        reject(error);-        return;+    let buffer;+    try {+      // Host-supplied CORS-bypassed binary fetch (FetchProxy under+      // Tauri; plain fetch under web hosts that allow it).+      buffer = await (+        await this.deps.fetchAsset(media_url, { signal })+      ).arrayBuffer();+    } catch (error) {+      if ((error as { name?: string })?.name !== "AbortError") {+        console.error("load GLB from Tauri error:", error);       }-      const glbLoader = new GLTFLoader();+      throw error;+    }+    const glbLoader = new GLTFLoader();+    return new Promise<GLTF>((resolve, reject) => {       glbLoader.parse(         buffer,         "", // NB: No relative path to load additional assets!@@ -1068,22 +1067,21 @@ class Scene {     onComplete: () => void,     signal?: AbortSignal,   ): Promise<SplatMesh> {-    return new Promise(async (resolve, reject) => {-      let buffer;-      try {-        // Host-supplied CORS-bypassed binary fetch (FetchProxy under-        // Tauri; plain fetch under web hosts that allow it).-        buffer = await (-          await this.deps.fetchAsset(media_url, { signal })-        ).arrayBuffer();-      } catch (error) {-        if ((error as { name?: string })?.name !== "AbortError") {-          console.error("load Splat from Tauri error:", error);-        }-        reject(error);-        return;+    let buffer;+    try {+      // Host-supplied CORS-bypassed binary fetch (FetchProxy under+      // Tauri; plain fetch under web hosts that allow it).+      buffer = await (+        await this.deps.fetchAsset(media_url, { signal })+      ).arrayBuffer();+    } catch (error) {+      if ((error as { name?: string })?.name !== "AbortError") {+        console.error("load Splat from Tauri error:", error);       }+      throw error;+    } +    return new Promise<SplatMesh>((resolve) => {       new SplatMesh({         fileBytes: buffer,         onLoad: (mesh) => {diff --git a/frontend/libs/components/pagescene/src/lib/engine/scene_manager_api.ts b/frontend/libs/components/pagescene/src/lib/engine/scene_manager_api.ts--- a/frontend/libs/components/pagescene/src/lib/engine/scene_manager_api.ts+++ b/frontend/libs/components/pagescene/src/lib/engine/scene_manager_api.ts@@ -32,15 +32,21 @@ export type SceneObject = { };  export interface SceneManagerAPI {-  create(media_token: string, name: string, position: THREE.Vector3): void;-  retrieve(object_uuid: string): void;-  delete(object_uuid: string): void;+  create(+    media_token: string,+    name: string,+    position: THREE.Vector3,+  ): Promise<THREE.Object3D<THREE.Object3DEventMap> | undefined>;+  retrieve(+    object_uuid: string,+  ): Promise<THREE.Object3D<THREE.Object3DEventMap> | undefined>;+  delete(object_uuid: string): Promise<void>;   update(     object_uuid: string,     position: THREE.Vector3,     rotation: THREE.Euler,     scale: THREE.Vector3,-  ): void;+  ): Promise<void>;   selected(): void;   render_outliner(timeline_characters: { [key: string]: ClipGroup }): void;   select_object(id: string): void;diff --git a/frontend/libs/components/upload-modal/src/lib/UploadModal3D/utilities/convertFbxToGlb.worker.ts b/frontend/libs/components/upload-modal/src/lib/UploadModal3D/utilities/convertFbxToGlb.worker.ts--- a/frontend/libs/components/upload-modal/src/lib/UploadModal3D/utilities/convertFbxToGlb.worker.ts+++ b/frontend/libs/components/upload-modal/src/lib/UploadModal3D/utilities/convertFbxToGlb.worker.ts@@ -23,26 +23,28 @@ const scope = self as unknown as {   postMessage: (message: ConvertResponse, transfer?: Transferable[]) => void; }; -scope.onmessage = async (event) => {-  const { id, buffer } = event.data;-  try {-    const group = new FBXLoader().parse(buffer, "");-    const glb = await new Promise<ArrayBuffer>((resolve, reject) => {-      new GLTFExporter().parse(-        group,-        (result) => {-          if (result instanceof ArrayBuffer) {-            resolve(result);-          } else {-            reject(new Error("GLTF export did not produce binary output."));-          }-        },-        (error) => reject(error),-        { binary: true, animations: group.animations ?? [] },-      );-    });-    scope.postMessage({ id, ok: true, glb }, [glb]);-  } catch (error) {-    scope.postMessage({ id, ok: false, error: String(error) });-  }+scope.onmessage = (event) => {+  void (async () => {+    const { id, buffer } = event.data;+    try {+      const group = new FBXLoader().parse(buffer, "");+      const glb = await new Promise<ArrayBuffer>((resolve, reject) => {+        new GLTFExporter().parse(+          group,+          (result) => {+            if (result instanceof ArrayBuffer) {+              resolve(result);+            } else {+              reject(new Error("GLTF export did not produce binary output."));+            }+          },+          (error) => reject(error),+          { binary: true, animations: group.animations ?? [] },+        );+      });+      scope.postMessage({ id, ok: true, glb }, [glb]);+    } catch (error) {+      scope.postMessage({ id, ok: false, error: String(error) });+    }+  })(); };diff --git a/frontend/libs/components/video-editor/src/lib/core/managers/audio-manager.ts b/frontend/libs/components/video-editor/src/lib/core/managers/audio-manager.ts--- a/frontend/libs/components/video-editor/src/lib/core/managers/audio-manager.ts+++ b/frontend/libs/components/video-editor/src/lib/core/managers/audio-manager.ts@@ -539,7 +539,7 @@ export class AudioManager {   }): Promise<AudioBuffer | null> {     const cacheKey = this.buildPreparedClipCacheKey({ clip });     const existing = this.preparedClipBuffers.get(cacheKey);-    if (existing) {+    if (existing !== undefined) {       return existing;     } @@ -574,7 +574,7 @@ export class AudioManager {     clip: AudioClipSource;   }): Promise<AudioBuffer | null> {     const existing = this.decodedBuffers.get(clip.sourceKey);-    if (existing) {+    if (existing !== undefined) {       return existing;     } diff --git a/frontend/libs/components/video-editor/src/lib/fonts/google-fonts.ts b/frontend/libs/components/video-editor/src/lib/fonts/google-fonts.ts--- a/frontend/libs/components/video-editor/src/lib/fonts/google-fonts.ts+++ b/frontend/libs/components/video-editor/src/lib/fonts/google-fonts.ts@@ -26,7 +26,7 @@ export function clearFontAtlasCache(): void {  export function loadFontAtlas(): Promise<FontAtlas | null> {   if (cachedAtlas) return Promise.resolve(cachedAtlas);-  if (atlasFetchPromise) return atlasFetchPromise;+  if (atlasFetchPromise !== null) return atlasFetchPromise;    atlasFetchPromise = fetch(FONT_ATLAS_PATH)     .then(async (response) => {diff --git a/frontend/libs/components/video-editor/src/lib/services/renderer/nodes/image-node.ts b/frontend/libs/components/video-editor/src/lib/services/renderer/nodes/image-node.ts--- a/frontend/libs/components/video-editor/src/lib/services/renderer/nodes/image-node.ts+++ b/frontend/libs/components/video-editor/src/lib/services/renderer/nodes/image-node.ts@@ -35,7 +35,7 @@ export function loadImageSource({   const cacheKey = `${url}::${maxSourceSize ?? "full"}`;    const cached = imageSourceCache.get(cacheKey);-  if (cached) return cached;+  if (cached !== undefined) return cached;    const promise = (async (): Promise<CachedImageSource> => {     const image = new Image();diff --git a/frontend/libs/components/video-editor/src/lib/services/renderer/nodes/sticker-node.ts b/frontend/libs/components/video-editor/src/lib/services/renderer/nodes/sticker-node.ts--- a/frontend/libs/components/video-editor/src/lib/services/renderer/nodes/sticker-node.ts+++ b/frontend/libs/components/video-editor/src/lib/services/renderer/nodes/sticker-node.ts@@ -25,7 +25,7 @@ export function loadStickerSource({   stickerId: string; }): Promise<CachedStickerSource> {   const cached = stickerSourceCache.get(stickerId);-  if (cached) return cached;+  if (cached !== undefined) return cached;    const promise = (async (): Promise<CachedStickerSource> => {     const url = resolveStickerId({diff --git a/frontend/libs/components/video-editor/src/lib/services/video-cache/service.ts b/frontend/libs/components/video-editor/src/lib/services/video-cache/service.ts--- a/frontend/libs/components/video-editor/src/lib/services/video-cache/service.ts+++ b/frontend/libs/components/video-editor/src/lib/services/video-cache/service.ts@@ -170,7 +170,7 @@ export class VideoCache {      try {       while (true) {-        if (sinkData.prefetching && sinkData.prefetchPromise) {+        if (sinkData.prefetching && sinkData.prefetchPromise !== null) {           await sinkData.prefetchPromise;         } @@ -215,7 +215,7 @@ export class VideoCache {     time: number;   }): Promise<WrappedCanvas | null> {     try {-      if (sinkData.prefetching && sinkData.prefetchPromise) {+      if (sinkData.prefetching && sinkData.prefetchPromise !== null) {         await sinkData.prefetchPromise;       } diff --git a/frontend/libs/components/video-editor/src/lib/services/waveform-cache/service.ts b/frontend/libs/components/video-editor/src/lib/services/waveform-cache/service.ts--- a/frontend/libs/components/video-editor/src/lib/services/waveform-cache/service.ts+++ b/frontend/libs/components/video-editor/src/lib/services/waveform-cache/service.ts@@ -27,7 +27,7 @@ export class WaveformCache {     audioUrl,   }: GetSourceWaveformSummaryArgs): Promise<SourceWaveformSummary> {     const existing = this.summaries.get(sourceKey);-    if (existing) {+    if (existing !== undefined) {       return existing;     }