@qvac/ocr-ggml 0.23.0 → 0.24.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": "@qvac/ocr-ggml",
3
- "version": "0.23.0",
3
+ "version": "0.24.1",
4
4
  "description": "GGML-backed OCR addon for qvac (EasyOCR and DocTR pipelines on GGUF weights)",
5
5
  "addon": true,
6
6
  "engines": {
@@ -95,7 +95,7 @@
95
95
  },
96
96
  "dependencies": {
97
97
  "@qvac/error": "^0.1.0",
98
- "@qvac/fabric": "^0.13.0",
98
+ "@qvac/fabric": "^0.15.0",
99
99
  "@qvac/infer-base": "^0.6.2",
100
100
  "@qvac/logging": "^0.1.0",
101
101
  "bare-fetch": "^3.0.1",
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.23"
10
+ "version": "0.24.1"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.23"
10
+ "version": "0.24.1"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.23"
10
+ "version": "0.24.1"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.23"
10
+ "version": "0.24.1"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  "current_versions": [
9
9
  {
10
- "version": "0.23"
10
+ "version": "0.24.1"
11
11
  }
12
12
  ],
13
13
  "exported_symbols": [
@@ -690,23 +690,55 @@ function prestagedModelPath(modelName) {
690
690
  return staged ? staged.src : null
691
691
  }
692
692
 
693
+ // iOS kills an app that dirties more than 4 GiB in 24h, and there the staged
694
+ // file already sits in the app's own writable Documents dir — so copying it
695
+ // into the model dir spends that whole budget for nothing. Hardlink instead:
696
+ // same inode, zero bytes. On Android the staging dir is a different filesystem,
697
+ // so link() fails EXDEV and we fall back to the copy that has always run there.
698
+ // `link`/`copy` are injectable so that fallback is unit-testable.
699
+ function linkOrCopySync({ src, dest, link = fs.linkSync, copy = fs.copyFileSync }) {
700
+ // Same path in and out — the staged file IS the destination (a caller whose
701
+ // model dir is testDir itself). Deleting first would destroy the staged model
702
+ // and leave both link() and copy() failing ENOENT; the copy this replaced was
703
+ // a harmless no-op here.
704
+ if (path.resolve(src) === path.resolve(dest)) return 'link'
705
+
706
+ try {
707
+ fs.unlinkSync(dest)
708
+ } catch (_) {}
709
+
710
+ try {
711
+ link(src, dest)
712
+ return 'link'
713
+ } catch (err) {
714
+ console.log(
715
+ `[prestage] hardlink failed on ${platform} (${err.message}); falling back to a byte copy`
716
+ )
717
+ }
718
+
719
+ copy(src, dest)
720
+ return 'copy'
721
+ }
722
+
693
723
  // The host pushes an exact byte-count sidecar with each model. Require both the
694
- // staged source and copied destination to match it so truncated adb/app copies
695
- // fall through to the network download.
724
+ // staged source and the materialised destination to match it so truncated
725
+ // adb/app transfers fall through to the network download.
696
726
  function copyPrestagedModel(modelName, destPath, minBytes = 1024 * 1024) {
697
727
  const staged = readPrestagedModel(modelName)
698
728
  if (!staged || staged.expectedSize < minBytes) return false
699
729
  try {
700
730
  const dir = path.dirname(destPath)
701
731
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
702
- fs.copyFileSync(staged.src, destPath)
732
+ const how = linkOrCopySync({ src: staged.src, dest: destPath })
703
733
  if (fs.statSync(destPath).size === staged.expectedSize) {
704
- console.log(`[prestage] Using pre-staged model ${modelName}`)
734
+ console.log(
735
+ `[prestage] Using pre-staged model ${modelName} (${how === 'link' ? 'hardlinked' : 'copied'})`
736
+ )
705
737
  return true
706
738
  }
707
739
  fs.unlinkSync(destPath)
708
740
  } catch (err) {
709
- console.log(`[prestage] copy of ${modelName} failed: ${err.message}`)
741
+ console.log(`[prestage] staging of ${modelName} failed: ${err.message}`)
710
742
  try {
711
743
  fs.unlinkSync(destPath)
712
744
  } catch (_) {}
@@ -1418,6 +1450,7 @@ module.exports = {
1418
1450
  ensureModelPath,
1419
1451
  ensureDoctrModels,
1420
1452
  copyPrestagedModel,
1453
+ linkOrCopySync,
1421
1454
  prestagedModelPath,
1422
1455
  GGML_MODELS_DIR,
1423
1456
  formatOCRPerformanceMetrics,