feat: add barcode scanner with OpenFoodFacts integration
All checks were successful
CI / update (push) Successful in 5m26s

- Camera-based barcode scanning in FoodSearch using barcode-detector (ZXing WASM)
- Import script to load OFF MongoDB dump into lean openfoodfacts collection
  with kJ→kcal fallback and dedup handling
- Barcode lookup API with live OFF API fallback that caches results locally,
  progressively enhancing the local database
- Add 'off' source to food log, custom meal, and favorite ingredient models
- OpenFoodFact mongoose model for the openfoodfacts collection
This commit is contained in:
2026-04-05 11:57:25 +02:00
parent c4420b73d2
commit b7397898e3
9 changed files with 882 additions and 23 deletions

49
pnpm-lock.yaml generated
View File

@@ -14,12 +14,18 @@ importers:
'@huggingface/transformers':
specifier: ^4.0.0
version: 4.0.0
'@nicolo-ribaudo/chokidar-2':
specifier: 2.1.8-no-fsevents.3
version: 2.1.8-no-fsevents.3
'@sveltejs/adapter-node':
specifier: ^5.0.0
version: 5.3.1(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(terser@5.46.0)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(terser@5.46.0)))
'@tauri-apps/plugin-geolocation':
specifier: ^2.3.2
version: 2.3.2
barcode-detector:
specifier: ^3.1.2
version: 3.1.2(@types/emscripten@1.41.5)
chart.js:
specifier: ^4.5.0
version: 4.5.0
@@ -807,6 +813,9 @@ packages:
'@mongodb-js/saslprep@1.3.0':
resolution: {integrity: sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==}
'@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3':
resolution: {integrity: sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==}
'@panva/hkdf@1.2.1':
resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==}
@@ -1160,6 +1169,9 @@ packages:
'@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
'@types/emscripten@1.41.5':
resolution: {integrity: sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==}
'@types/estree@1.0.1':
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
@@ -1257,6 +1269,9 @@ packages:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
engines: {node: '>= 0.4'}
barcode-detector@3.1.2:
resolution: {integrity: sha512-Q5kjXpVH5I3ItykNzbWmfWnNryFN1ZTWp10k9/PKJuS0RnoKR7jTrHEJODR4fn04bRomq7TJwie/Dr9fj/GoGQ==}
bidi-js@1.0.3:
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
@@ -1959,6 +1974,10 @@ packages:
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
tagged-tag@1.0.0:
resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
engines: {node: '>=20'}
terser@5.46.0:
resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==}
engines: {node: '>=10'}
@@ -2016,6 +2035,10 @@ packages:
resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
engines: {node: '>=10'}
type-fest@5.5.0:
resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==}
engines: {node: '>=20'}
typescript@5.1.6:
resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
engines: {node: '>=14.17'}
@@ -2210,6 +2233,11 @@ packages:
zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
zxing-wasm@3.0.2:
resolution: {integrity: sha512-2YMAriaYHX9wrBY2k7H0epSo+dyCaCZg/vOtt+nEDXM9ul480gkXz/9SkwpOeHcD2H5qqDG8lWDSBwpTcZpa6w==}
peerDependencies:
'@types/emscripten': '>=1.39.6'
snapshots:
'@acemir/cssom@0.9.23': {}
@@ -2666,6 +2694,8 @@ snapshots:
dependencies:
sparse-bitfield: 3.0.3
'@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3': {}
'@panva/hkdf@1.2.1': {}
'@playwright/test@1.56.1':
@@ -2959,6 +2989,8 @@ snapshots:
'@types/deep-eql@4.0.2': {}
'@types/emscripten@1.41.5': {}
'@types/estree@1.0.1': {}
'@types/estree@1.0.8': {}
@@ -3053,6 +3085,12 @@ snapshots:
axobject-query@4.1.0: {}
barcode-detector@3.1.2(@types/emscripten@1.41.5):
dependencies:
zxing-wasm: 3.0.2(@types/emscripten@1.41.5)
transitivePeerDependencies:
- '@types/emscripten'
bidi-js@1.0.3:
dependencies:
require-from-string: 2.0.2
@@ -3820,6 +3858,8 @@ snapshots:
symbol-tree@3.2.4: {}
tagged-tag@1.0.0: {}
terser@5.46.0:
dependencies:
'@jridgewell/source-map': 0.3.11
@@ -3873,6 +3913,10 @@ snapshots:
type-fest@0.13.1: {}
type-fest@5.5.0:
dependencies:
tagged-tag: 1.0.0
typescript@5.1.6: {}
uint8array-extras@1.5.0: {}
@@ -4005,3 +4049,8 @@ snapshots:
xmlchars@2.2.0: {}
zimmerframe@1.1.2: {}
zxing-wasm@3.0.2(@types/emscripten@1.41.5):
dependencies:
'@types/emscripten': 1.41.5
type-fest: 5.5.0