Summary
#71 fixed one .web shim that had drifted from the module it stands in for. Diffing every native/.web pair afterwards shows it was not an isolated slip: 10 more exports are missing across five shims, and four of them are reachable from screens that have no .web companion, so they fail the same way — TypeError: x is not a function, only at the moment the user triggers the action.
Filing separately rather than widening #71, which is closed.
The gap
Measured on bcddff2:
| shim |
exports missing relative to the native module |
src/database/repositories/videoRepository.web.ts |
bulkSetFavorite, deleteVideos, getExistingAssetIds, getVideosWithSuspiciousCapturedAt, updateVideoThumbnailUri |
src/database/repositories/tagRepository.web.ts |
deleteCustomTag |
src/database/repositories/techniqueOptionRepository.web.ts |
reorderTechniqueOptions |
src/services/managedVideoFileService.web.ts |
getManagedVideoDirectoryUri |
src/services/thumbnailService.web.ts |
getThumbnailDirectoryUri |
Which of these actually break
Traced each importer and checked whether it has a .web variant of its own. Only the ones without can reach the shim.
Reachable on Web — these are live bugs:
| export |
importer without a .web companion |
user-visible action |
deleteVideos |
src/app/(tabs)/index/index.tsx, src/app/settings/duplicate-candidates.tsx, src/services/videoDeletionService.ts |
bulk delete from home, delete from duplicate candidates |
bulkSetFavorite |
src/app/(tabs)/index/index.tsx |
bulk favourite from home |
deleteCustomTag |
src/app/settings/tags.tsx |
delete a custom tag |
reorderTechniqueOptions |
src/app/settings/techniques.tsx |
drag to reorder techniques |
deleteVideos is the worst of the four: it is the same bulk-delete path #71 was about, so that path is still broken on Web even after isSyntheticAssetId was fixed.
Not reachable — every importer resolves to its own .web file, so the native module never loads:
getExistingAssetIds (video-import.tsx), getVideosWithSuspiciousCapturedAt (_layout.tsx), updateVideoThumbnailUri (thumbnailMigrationService.ts), getManagedVideoDirectoryUri and getThumbnailDirectoryUri (orphanedFileCleanupService.ts, importService.ts).
These are still worth closing for consistency — today's safe gap becomes tomorrow's bug the moment a new caller lacks a .web file — but they are not urgent.
Why nothing catches this
npx tsc --noEmit is clean. TypeScript resolves ./videoRepository to videoRepository.ts, which does export everything. Only Metro substitutes the .web variant at bundle time, so the type checker never compares the two files. npm run lint does not compare them either.
The failure is also silent until the exact moment of use: all four live cases sit in callbacks, so the screen renders normally and the throw lands on the user's click.
Suggested approach
- Fill the four reachable gaps first — they are user-visible.
- Fill the remaining five for parity.
- Add a mechanical check so the next drift is caught rather than discovered. A
node:test case fits the existing scripts/tests/ setup and needs no new tooling — it can compare exported names by reading both files, with no compilation:
for f in src/database/repositories/*.ts src/services/*.ts; do
case "$f" in *.web.ts) continue;; esac
w="${f%.ts}.web.ts"; [ -f "$w" ] || continue
diff <(grep -oE "^export (async )?function [a-zA-Z]+" "$f" | grep -oE "[a-zA-Z]+$" | sort) \
<(grep -oE "^export (async )?function [a-zA-Z]+" "$w" | grep -oE "[a-zA-Z]+$" | sort)
done
Note this shell version only sees export function / export async function declarations. A test should also handle export const and export { ... } from, which is how mediaService.ts exposes isSyntheticAssetId after #71 — a checker that missed re-exports would report a false positive there.
Acceptance criteria
- Every
.web shim exports at least what its native counterpart does.
- Bulk delete, bulk favourite, custom-tag delete and technique reorder all work on Web without a
TypeError.
- A check fails when a native module gains an export its
.web shim lacks, and it understands re-exports.
- Native behaviour is unchanged.
Related
Summary
#71 fixed one
.webshim that had drifted from the module it stands in for. Diffing every native/.webpair afterwards shows it was not an isolated slip: 10 more exports are missing across five shims, and four of them are reachable from screens that have no.webcompanion, so they fail the same way —TypeError: x is not a function, only at the moment the user triggers the action.Filing separately rather than widening #71, which is closed.
The gap
Measured on
bcddff2:src/database/repositories/videoRepository.web.tsbulkSetFavorite,deleteVideos,getExistingAssetIds,getVideosWithSuspiciousCapturedAt,updateVideoThumbnailUrisrc/database/repositories/tagRepository.web.tsdeleteCustomTagsrc/database/repositories/techniqueOptionRepository.web.tsreorderTechniqueOptionssrc/services/managedVideoFileService.web.tsgetManagedVideoDirectoryUrisrc/services/thumbnailService.web.tsgetThumbnailDirectoryUriWhich of these actually break
Traced each importer and checked whether it has a
.webvariant of its own. Only the ones without can reach the shim.Reachable on Web — these are live bugs:
.webcompaniondeleteVideossrc/app/(tabs)/index/index.tsx,src/app/settings/duplicate-candidates.tsx,src/services/videoDeletionService.tsbulkSetFavoritesrc/app/(tabs)/index/index.tsxdeleteCustomTagsrc/app/settings/tags.tsxreorderTechniqueOptionssrc/app/settings/techniques.tsxdeleteVideosis the worst of the four: it is the same bulk-delete path #71 was about, so that path is still broken on Web even afterisSyntheticAssetIdwas fixed.Not reachable — every importer resolves to its own
.webfile, so the native module never loads:getExistingAssetIds(video-import.tsx),getVideosWithSuspiciousCapturedAt(_layout.tsx),updateVideoThumbnailUri(thumbnailMigrationService.ts),getManagedVideoDirectoryUriandgetThumbnailDirectoryUri(orphanedFileCleanupService.ts,importService.ts).These are still worth closing for consistency — today's safe gap becomes tomorrow's bug the moment a new caller lacks a
.webfile — but they are not urgent.Why nothing catches this
npx tsc --noEmitis clean. TypeScript resolves./videoRepositorytovideoRepository.ts, which does export everything. Only Metro substitutes the.webvariant at bundle time, so the type checker never compares the two files.npm run lintdoes not compare them either.The failure is also silent until the exact moment of use: all four live cases sit in callbacks, so the screen renders normally and the throw lands on the user's click.
Suggested approach
node:testcase fits the existingscripts/tests/setup and needs no new tooling — it can compare exported names by reading both files, with no compilation:Note this shell version only sees
export function/export async functiondeclarations. A test should also handleexport constandexport { ... } from, which is howmediaService.tsexposesisSyntheticAssetIdafter #71 — a checker that missed re-exports would report a false positive there.Acceptance criteria
.webshim exports at least what its native counterpart does.TypeError..webshim lacks, and it understands re-exports.Related
.memory/doc-drift.mdwith the re-check command.