pinokiod 8.0.41 → 8.0.42

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": "pinokiod",
3
- "version": "8.0.41",
3
+ "version": "8.0.42",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -26,6 +26,7 @@ const COPY = {
26
26
  before_help: "Estimated space if every app stored its own copy.",
27
27
  after: "After",
28
28
  effective_help: "Shared files are divided evenly among every location using them.",
29
+ partial_metrics_help: "Based on the latest scan. Unreadable paths may not be included.",
29
30
  no_more_matches: "No more matches in files {size} and larger",
30
31
  more_can_be_saved: "{size} more can be saved",
31
32
  available_to_save: "{size} available to save",
@@ -925,6 +926,8 @@ const renderOverview = () => {
925
926
  const lastAttempt = data.last_attempt || last
926
927
  const incomplete = !!data.incomplete
927
928
  const metricsExact = !!last && !incomplete && data.metrics_exact !== false
929
+ const metricScan = metricsExact ? last : lastAttempt
930
+ const metricsAvailable = !!(metricScan && Number.isFinite(metricScan.bytes_total))
928
931
  const activeScan = scanActive(data.scan)
929
932
  const scanning = scanMatchesContext(data.scan)
930
933
  const busyElsewhere = activeScan && !scanning
@@ -934,17 +937,22 @@ const renderOverview = () => {
934
937
  {
935
938
  const beforeBytes = Math.max(0, Number.isFinite(data.bytes_without_sharing)
936
939
  ? data.bytes_without_sharing
937
- : (data.last_scan && Number.isFinite(data.last_scan.bytes_total)
938
- ? data.last_scan.bytes_total
940
+ : (metricScan && Number.isFinite(metricScan.bytes_total)
941
+ ? metricScan.bytes_total
939
942
  : 0))
940
- const afterBytes = Math.max(0, Number.isFinite(data.bytes_on_disk)
941
- ? data.bytes_on_disk
942
- : beforeBytes)
943
- const afterRatio = beforeBytes ? Math.min(100, (afterBytes / beforeBytes) * 100) : 0
944
943
  const pendingBytes = Math.max(0, Number(data.pending_bytes) || 0)
944
+ const reportedAfterBytes = Number.isFinite(data.bytes_on_disk)
945
+ ? Math.max(0, data.bytes_on_disk)
946
+ : null
945
947
  const savedBytes = Number.isFinite(data.saved_by_sharing)
946
948
  ? Math.max(0, data.saved_by_sharing)
947
- : (IS_APP_MODE ? Math.max(0, beforeBytes - afterBytes) : 0)
949
+ : (IS_APP_MODE && reportedAfterBytes !== null
950
+ ? Math.max(0, beforeBytes - reportedAfterBytes)
951
+ : 0)
952
+ const afterBytes = reportedAfterBytes === null
953
+ ? Math.max(0, beforeBytes - savedBytes)
954
+ : reportedAfterBytes
955
+ const afterRatio = beforeBytes ? Math.min(100, (afterBytes / beforeBytes) * 100) : 0
948
956
  const headline = incomplete
949
957
  ? (savedBytes
950
958
  ? COPY.at_least_saved.replace("{size}", fmt(savedBytes))
@@ -956,10 +964,12 @@ const renderOverview = () => {
956
964
  : `${fmt(savedBytes)} ${COPY.disk_space_saved}`)
957
965
  : COPY.no_space_saved)
958
966
  : (IS_APP_MODE ? COPY.find_app_savings : COPY.find_savings)
959
- const help = IS_APP_MODE ? COPY.effective_help : COPY.before_help
967
+ const help = incomplete
968
+ ? COPY.partial_metrics_help
969
+ : IS_APP_MODE ? COPY.effective_help : COPY.before_help
960
970
  const helpId = IS_APP_MODE ? "vault-after-help" : "vault-before-help"
961
971
  const helpMarkup = `<button class="vault-compare-info" type="button" aria-describedby="${helpId}" aria-label="${attr(help)}"><i class="fa-regular fa-circle-question" aria-hidden="true"></i></button>`
962
- const comparison = metricsExact ? `
972
+ const comparison = metricsAvailable ? `
963
973
  <div class="vault-comparison" aria-label="${attr(`${COPY.before}: ${fmt(beforeBytes)}. ${COPY.after}: ${fmt(afterBytes)}.`)}">
964
974
  <div class="vault-compare-row">
965
975
  <span class="vault-compare-label">${esc(COPY.before)}${IS_APP_MODE ? "" : helpMarkup}</span>
@@ -992,7 +1002,7 @@ const renderOverview = () => {
992
1002
  </div>
993
1003
  <div class="vault-summary-side">${opportunity}${opportunity ? `<span class="vault-summary-divider" aria-hidden="true"></span>` : ""}<span class="vault-summary-freshness">${esc(freshness)}</span></div>`
994
1004
  document.querySelectorAll("body > .vault-compare-tooltip").forEach((tooltip) => tooltip.remove())
995
- if (metricsExact) {
1005
+ if (metricsAvailable) {
996
1006
  const tooltip = document.createElement("span")
997
1007
  tooltip.id = helpId
998
1008
  tooltip.className = "vault-compare-tooltip"
@@ -1445,6 +1445,65 @@ describe('vault dashboard backend (phase 4)', () => {
1445
1445
  dom.window.close()
1446
1446
  })
1447
1447
 
1448
+ test('an incomplete scan keeps a qualified Before and After comparison', async () => {
1449
+ const views = path.resolve(__dirname, '..', 'server', 'views')
1450
+ const workspace = await ejs.renderFile(
1451
+ path.resolve(views, 'partials', 'vault_workspace.ejs'),
1452
+ { appMode: false }
1453
+ )
1454
+ const dom = new JSDOM(`<body data-vault-mode="global">${workspace}</body>`, {
1455
+ url: 'http://localhost/vault', runScripts: 'dangerously'
1456
+ })
1457
+ const gb = 1024 ** 3
1458
+ dom.window.fetch = async () => ({
1459
+ ok: true,
1460
+ json: async () => ({
1461
+ enabled: true,
1462
+ mode: 'link',
1463
+ scan: { active: false, pending: false, queued: 0, phase: 'incomplete', scope_id: null },
1464
+ last_scan: null,
1465
+ last_attempt: {
1466
+ ts: Date.now(),
1467
+ complete: false,
1468
+ bytes_total: 500 * gb,
1469
+ unreadable: [{ path: '/pinokio/api/app/private', code: 'EACCES' }]
1470
+ },
1471
+ incomplete: true,
1472
+ metrics_exact: false,
1473
+ bytes_on_disk: null,
1474
+ bytes_without_sharing: null,
1475
+ saved_by_sharing: 100 * gb,
1476
+ pending_bytes: 0,
1477
+ reclaimable: 0,
1478
+ activity_error: null,
1479
+ cloud_sync_warning: null,
1480
+ sources: [],
1481
+ blobs: [],
1482
+ duplicates: [],
1483
+ excluded: [],
1484
+ events: [],
1485
+ undo_batches: []
1486
+ })
1487
+ })
1488
+ await runVaultScript(dom)
1489
+ await new Promise((resolve) => setTimeout(resolve, 25))
1490
+
1491
+ assert.strictEqual(
1492
+ dom.window.document.querySelector('.vault-summary-value').textContent,
1493
+ 'At least 107.37 GB saved'
1494
+ )
1495
+ assert.deepStrictEqual(
1496
+ [...dom.window.document.querySelectorAll('.vault-compare-value')].map((node) => node.textContent),
1497
+ ['536.87 GB', '429.5 GB']
1498
+ )
1499
+ assert.strictEqual(
1500
+ dom.window.document.getElementById('vault-before-help').textContent,
1501
+ 'Based on the latest scan. Unreadable paths may not be included.'
1502
+ )
1503
+ assert.match(dom.window.document.querySelector('.vault-result').textContent, /Scan incomplete/)
1504
+ dom.window.close()
1505
+ })
1506
+
1448
1507
  test('undo sharing explains each safe refusal instead of showing a generic failure', async () => {
1449
1508
  const source = await fs.promises.readFile(path.resolve(__dirname, '..', 'server', 'public', 'vault.js'), 'utf8')
1450
1509
  assert.match(source, /const detachFeedback = \(result\) =>/)