Use platform-native file change APIs to track which stored-file
attachments have been modified, avoiding expensive full scans of all
attachment files during sync.
Backends:
- macOS: FSEvents (persistent event journal, survives restarts)
- Windows: ReadDirectoryChangesW (live recursive directory watch
via overlapped I/O polling)
- Linux: inotify (live per-directory watches)
On unsupported platforms or on error, falls back to existing scan logic.
Don't mark attachments for download when server has no file (mtime is
null), and clear TO_DOWNLOAD state on 404 responses in both ZFS and
WebDAV so items aren't re-checked on every subsequent sync.
Move _ObjectType, _objectTypePlural, _ObjectTypePlural, and
_ObjectsClass from per-instance properties to prototype properties,
computed once per subclass on first instantiation. This avoids a huge
number of duplicate string allocations and reduce memory usage a bit
(e.g., ~35 MB in a 19K-item test library).
setOutputFormat() just sets this.opt.mode and this.fun.decorate, so
there shouldn't be a need to cache separate engines per format. The
format is excluded from the cache key, and setOutputFormat() is called
on cache hit instead.
Also simplify _preloadFormat() in quickCopy.js to only create one
engine instead of two (one per format).
Saves ~7 MB of resident memory (~140 MB before the citeproc-js
sort-macro caching optimization).
Previously, macros referenced by sort keys were expanded inline into
the sort token array every time they were encountered. Non-sort macros
were already cached in state.macros[name] and referenced via closures,
but sort-mode macros were duplicated across sort keys.
With a complex style like chicago-shortened-notes-bibliography (155
macros, 6 macro-based sort keys), this produced 179,605 tokens and
355,652 closures in bibliography_sort alone -- 99.9% of all tokens.
This commit caches sort-mode macro expansions in state.sort_macros[name]
using the same closure-reference approach as non-sort macros.
In addition to the memory savings, avoiding the redundant macro
expansion makes engine initialization dramatically faster with complex
styles.
Results with chicago-shortened-notes-bibliography:
- Engine initialization: 464 ms -> 57 ms (8x faster)
- Per-engine heap (Node.js): 123 MB -> 6.7 MB (94.6% reduction)
- Resident per engine (Firefox): ~140 MB -> ~7 MB
- Total tokens: 187,893 -> 11,669 (93.8% reduction)
- Total closures: 370,982 -> 21,823 (94.1% reduction)
- All 1,502 citeproc-js tests pass (at f88a47e6)
- Same 1501/1505 pass on master (same 4 pre-existing failures)
The previous approach of capturing and replaying the Authorization header
from an initial request worked for Basic auth but broke with Digest auth,
where the header includes the HTTP method and URI in the hash. This caused
each replayed request to be rejected with a 401, which Firefox wouldn't
retry because the request already included Authorization.
Instead, split _channelAuthorization into _basicAuthHeader and
_digestParams. For Basic auth, the header is still replayed as-is. For
Digest, the challenge parameters (realm, nonce, qop, etc.) are cached
and a fresh Authorization header is computed per request.
_getAuthorizationHeaders() now takes method and URI parameters so it can
compute the correct Digest response hash for each request.
By adding a missing getter override. If a setter is overridden, the
corresponding getter also needs to be overridden, or it will implicitly
return undefined.
Fixes#5793
Failed when running CollectionTree and ZoteroPane tests together
- Make `deleteSelectedItems()` async and await `deleteSelection()` so
the full Notifier commit chain (including items list refresh)
completes before the function resolves. Update tests to call
`deleteSelectedItems()` directly instead of calling fire-and-forget
keyboard events, and add a stubbed test for the Delete key binding.
- Fix "Delete Item…" menu test to explicitly re-select after `saveTx()`
to guarantee selection state
`checkServer()` was capturing the Authorization header from OPTIONS and
explicitly setting it on PROPFIND via `setRequestHeader()`. If the auth
type didn't match what the server required for PROPFIND (e.g., Basic vs.
Digest, though maybe other things too), the explicit header could
prevent Firefox from negotiating the correct auth scheme on the 401
challenge.
To fix, move `onAuthorizationHeader()` from OPTIONS to PROPFIND and
don't pass captured auth to PROPFIND, letting Firefox handle the auth
transparently.
https://forums.zotero.org/discussion/129665/webdav-error-for-zotero-8-0-3
Some WebDAV servers allow unauthenticated OPTIONS requests, so the
Authorization header capturing added in 089701eca8 wouldn't work.
PROPFIND with Depth: 0 reliably requires authentication while only
returning properties of the directory itself.
https://forums.zotero.org/discussion/comment/506993/#Comment_506993
- on all platforms, Space will open the menulist popup
- on Windows, ArrowUp/ArrowDown will change selection without triggering command event.
This is needed to avoid alerts that are meant to fire after a confirmed
selection when the user is just trying to navigate the menu (e.g. item type menu)
- on Windows, fix odd appearance of checked menulist options in advanced search
condition dropdowns when "More" option is selected
- on Linux, fix menulist focus ring not appearing in Advanced Search
or Preferences (https://forums.zotero.org/discussion/129498/advanced-search-drop-down-menus-are-not-highlighted-during-keyboard-navigation)
Fixes: #5772
Firefox no longer seems to send a previously used Authorization header
with subsequent requests. This results in extra requests, since every
WebDAV request triggers a 401, and also results in errors, because a PUT
is sent without Authorization, causing some WebDAV servers to
immediately send a 401 and close the connection, which the HTTP layer
interprets as a connection failure (status 0). (It's also not good to
try to send a large file just to get a 401.)
There might be some way to share context between requests, but instead,
just get the used Authorization header and include that explicitly in
future requests.
To test this properly, we have to switch to using httpd.js for all
WebDAV requests, since the mocked XHR doesn't trigger the 401 retry.
https://forums.zotero.org/discussion/129194/webdav-uploads-fail-on-zotero-8-put-sent-without-authorization-server-closes-connection
Any modification to an item belonging to collections in the trash would
cause those collections to be removed from the item when the item was
uploaded.
Fixes#5766