From 9c7580f2c55c1ddf82386c79036b162c7bab02ac Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Mon, 18 Nov 2024 20:44:45 +0100 Subject: [PATCH 01/26] feedback for legacy formulas --- content/Preferences.ts | 6 ++- content/better-bibtex.ts | 1 - content/key-manager/formatter.ts | 20 ++++++++++ test/features/export.feature | 1 + ...change citation key formula #3058.biblatex | 0 ...not change citation key formula #3058.json | 40 +++++++++++++++++++ 6 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/export/Cannot change citation key formula #3058.biblatex create mode 100644 test/fixtures/export/Cannot change citation key formula #3058.json diff --git a/content/Preferences.ts b/content/Preferences.ts index c3603f69a8..4a0718a968 100644 --- a/content/Preferences.ts +++ b/content/Preferences.ts @@ -413,19 +413,21 @@ export class PrefPane { public checkCitekeyFormat(): void { if (!$window || Zotero.BetterBibTeX.starting) return // itemTypes not available yet - const error = Formatter.update([ Preference.citekeyFormatEditing, Preference.citekeyFormat ]) + const error = Formatter.test(Preference.citekeyFormatEditing || Preference.citekeyFormat) const editing = $window.document.getElementById('bbt-preferences-citekeyFormatEditing') editing.classList[error ? 'add' : 'remove']('bbt-prefs-error') editing.setAttribute(client.is7 ? 'title' : 'tooltiptext', error) if (client.is7) editing.setAttribute('tooltip', 'html-tooltip') const msg = $window.document.getElementById('bbt-citekeyFormat-error') as HTMLInputElement - msg.value = error + msg.value = error || (Preference.citekeyFormatEditing === '[' && 'legacy formula, will be upgraded when completed') msg.style.display = error ? 'initial' : 'none' const active = $window.document.getElementById('bbt-preferences-citekeyFormat') const label = $window.document.getElementById('bbt-label-citekeyFormat') active.style.display = label.style.display = Preference.citekeyFormat === Preference.citekeyFormatEditing ? 'none' : 'initial' + + if (!error) Formatter.update([ Preference.citekeyFormatEditing, Preference.citekeyFormat ]) } public checkPostscript(): void { diff --git a/content/better-bibtex.ts b/content/better-bibtex.ts index 709fe6d052..343173a590 100644 --- a/content/better-bibtex.ts +++ b/content/better-bibtex.ts @@ -881,7 +881,6 @@ export class BetterBibTeX { pinned.textContent = citekey.pinned ? icons.pin : '' setSectionSummary(citekey || '') - flash('item display', `item ${item?.id} displayed`) }, onInit: ({ body, refresh }) => { $done = Events.on('items-changed', ({ items }) => { diff --git a/content/key-manager/formatter.ts b/content/key-manager/formatter.ts index 14578c090a..045ceb9afe 100644 --- a/content/key-manager/formatter.ts +++ b/content/key-manager/formatter.ts @@ -382,6 +382,25 @@ export class PatternFormatter { }) } + public test(formula: string): string { + if (formula[0] === '[') { + try { + legacyparser.parse(formula, { reserved, items, methods }) + } + catch (err) { + return err.message as string + } + return '' + } + try { + this.parseFormula(formula) + } + catch (err) { + return err.message as string + } + return '' + } + // private fold: boolean public update(formulas: string[]): string { const unsafechars = rescape(Preference.citekeyUnsafeChars + '\uFFFD') @@ -399,6 +418,7 @@ export class PatternFormatter { if (formula[0] === '[') { try { formula = legacyparser.parse(formula, { reserved, items, methods }) + log.debug('legacy formula:', formula) } catch (err) { log.error(`formula-update: ${ ts } legacy-formula failed to upgrade ${ formula }: ${ err.message }`) diff --git a/test/features/export.feature b/test/features/export.feature index 24368555b0..6f36e93704 100644 --- a/test/features/export.feature +++ b/test/features/export.feature @@ -13,6 +13,7 @@ Feature: Export Examples: | file | references | + | Cannot change citation key formula #3058 | 1 | | lastpage not work in better bitex #3050 | 1 | | citekey not skip one-letter word #3021 | 1 | | charmapcsv mapping not working anymore #3020 | 1 | diff --git a/test/fixtures/export/Cannot change citation key formula #3058.biblatex b/test/fixtures/export/Cannot change citation key formula #3058.biblatex new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/export/Cannot change citation key formula #3058.json b/test/fixtures/export/Cannot change citation key formula #3058.json new file mode 100644 index 0000000000..1a7ad33cd7 --- /dev/null +++ b/test/fixtures/export/Cannot change citation key formula #3058.json @@ -0,0 +1,40 @@ +{ + "config": { + "id": "36a3b0b5-bad0-4a04-b79b-441c7cef77db", + "label": "BetterBibTeX JSON", + "preferences": { + "citekeyFormat": "[(auth.etal || shorttitle(3,0) || title(3,0):lower]_[year]" + } + }, + "items": [ + { + "ISBN": "978-0-646-55621-5", + "citationKey": "McDougallTJ.Barker-delete-me", + "creators": [ + { + "creatorType": "author", + "firstName": "Trevor J", + "lastName": "McDougall" + }, + { + "creatorType": "author", + "firstName": "Paul M", + "lastName": "Barker" + } + ], + "date": "2011", + "extra": [ + "OCLC: 724024071", + "Citation Key: McDougallTJ.Barker-delete-me" + ], + "itemID": 1, + "itemType": "book", + "language": "English", + "libraryCatalog": "Open WorldCat", + "place": "Battery Point, Tasmania, Australia", + "publisher": "Trevor J McDougall", + "title": "Getting started with TEOS-10 and the Gibbs Seawater (GSW) Oceanographic Toolbox", + "url": "https://www.teos-10.org/pubs/Getting_Started.pdf" + } + ] +} From a1fff5c206ab9aa66a0e72197715eb65ce034b08 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 11:55:48 +0100 Subject: [PATCH 02/26] contract words --- content/key-manager/formatter.ts | 21 ++++---- test/features/export.feature | 1 + ...n words if hyphens are used #3059.biblatex | 13 +++++ ...rtain words if hyphens are used #3059.json | 52 +++++++++++++++++++ 4 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 test/fixtures/export/Citation keys are missing certain words if hyphens are used #3059.biblatex create mode 100644 test/fixtures/export/Citation keys are missing certain words if hyphens are used #3059.json diff --git a/content/key-manager/formatter.ts b/content/key-manager/formatter.ts index 14578c090a..c97b20b0ac 100644 --- a/content/key-manager/formatter.ts +++ b/content/key-manager/formatter.ts @@ -1426,23 +1426,20 @@ export class PatternFormatter { } private contract(sentences: { terms: Term[] }[]): string[] { - const $terms: Term[] = [] + const terms: Term[] = [] + let tail: Term for (const sentence of sentences) { - let first = true - for (const term of sentence.terms) { - if (this.skipWords.has(term.text.toLowerCase())) continue - - if (first || ($terms[0].post && $terms[0].post !== '-')) { - $terms.unshift(term) + sentence.terms.forEach((term, i) => { + if (i !== 0 && tail.post === '-') { + tail.text += tail.post + term.text + tail.post = term.post } else { - $terms[0].text += $terms[0].post + term.text - $terms[0].post = term.post + terms.push(tail = term) } - first = false - } + }) } - return $terms.reverse().map(t => t.text) + return terms.map(term => term.text).filter(term => !this.skipWords.has(term.toLowerCase())) } private titleWords(title, options: { transliterate?: boolean; skipWords?: boolean; nopunct?: boolean } = {}): string[] { diff --git a/test/features/export.feature b/test/features/export.feature index 24368555b0..16ff8c77d0 100644 --- a/test/features/export.feature +++ b/test/features/export.feature @@ -13,6 +13,7 @@ Feature: Export Examples: | file | references | + | Citation keys are missing certain words if hyphens are used #3059 | 1 | | lastpage not work in better bitex #3050 | 1 | | citekey not skip one-letter word #3021 | 1 | | charmapcsv mapping not working anymore #3020 | 1 | diff --git a/test/fixtures/export/Citation keys are missing certain words if hyphens are used #3059.biblatex b/test/fixtures/export/Citation keys are missing certain words if hyphens are used #3059.biblatex new file mode 100644 index 0000000000..6ab1cc1670 --- /dev/null +++ b/test/fixtures/export/Citation keys are missing certain words if hyphens are used #3059.biblatex @@ -0,0 +1,13 @@ +@inproceedings{haarnojaSoftActorCriticOffPolicy2018, + title = {Soft {{Actor-Critic}}: {{Off-Policy Maximum Entropy Deep Reinforcement Learning}} with a {{Stochastic Actor}}}, + shorttitle = {Soft {{Actor-Critic}}}, + booktitle = {Proceedings of the 35th International Conference on Machine Learning ({{ICML}}'18)}, + author = {Haarnoja, Tuomas and Zhou, Aurick and Abbeel, Pieter and Levine, Sergey}, + date = {2018-07-03}, + pages = {1861--1870}, + location = {Stockholm, Sweden}, + url = {http://proceedings.mlr.press/v80/haarnoja18b.html}, + urldate = {2021-03-01}, + abstract = {Model-free deep reinforcement learning (RL) algorithms have been demonstrated on a range of challenging decision making and control tasks. However, these methods typically suffer from two major cha...}, + langid = {english} +} diff --git a/test/fixtures/export/Citation keys are missing certain words if hyphens are used #3059.json b/test/fixtures/export/Citation keys are missing certain words if hyphens are used #3059.json new file mode 100644 index 0000000000..b493811173 --- /dev/null +++ b/test/fixtures/export/Citation keys are missing certain words if hyphens are used #3059.json @@ -0,0 +1,52 @@ +{ + "config": { + "id": "36a3b0b5-bad0-4a04-b79b-441c7cef77db", + "label": "BetterBibTeX JSON", + "preferences": { + "baseAttachmentPath": "/home/trevor/Insync/trevor.ablett@gmail.com/Google Drive/UofT/Papers/zotero" + } + }, + "items": [ + { + "abstractNote": "Model-free deep reinforcement learning (RL) algorithms have been demonstrated on a range of challenging decision making and control tasks. However, these methods typically suffer from two major cha...", + "accessDate": "2021-03-01T14:42:36Z", + "citationKey": "haarnojaSoftActorCriticPolicy2018", + "creators": [ + { + "creatorType": "author", + "firstName": "Tuomas", + "lastName": "Haarnoja" + }, + { + "creatorType": "author", + "firstName": "Aurick", + "lastName": "Zhou" + }, + { + "creatorType": "author", + "firstName": "Pieter", + "lastName": "Abbeel" + }, + { + "creatorType": "author", + "firstName": "Sergey", + "lastName": "Levine" + } + ], + "date": "2018/07/03", + "itemID": 1, + "itemType": "conferencePaper", + "language": "en", + "libraryCatalog": "proceedings.mlr.press", + "notes": [ + "Comment: ICML 2018 Videos: sites.google.com/view/soft-actor-critic Code: github.com/haarnoja/sac" + ], + "pages": "1861-1870", + "place": "Stockholm, Sweden", + "publicationTitle": "Proceedings of the 35th international conference on machine learning (ICML'18)", + "shortTitle": "Soft Actor-Critic", + "title": "Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning with a Stochastic Actor", + "url": "http://proceedings.mlr.press/v80/haarnoja18b.html" + } + ] +} \ No newline at end of file From 08e7fcbf1eb4a1d35dea1cbf50bfbb494107bcd7 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 11:58:02 +0100 Subject: [PATCH 03/26] New Crowdin updates (#3063) * New translations better-bibtex.ftl (French) * New translations better-bibtex.ftl (German) * New translations better-bibtex.ftl (Italian) * New translations better-bibtex.ftl (Chinese Simplified) * New translations better-bibtex.ftl (Portuguese, Brazilian) --- locale/de-DE/better-bibtex.ftl | 2 -- locale/fr-FR/better-bibtex.ftl | 2 -- locale/it-IT/better-bibtex.ftl | 2 -- locale/pt-BR/better-bibtex.ftl | 2 -- locale/zh-CN/better-bibtex.ftl | 2 -- 5 files changed, 10 deletions(-) diff --git a/locale/de-DE/better-bibtex.ftl b/locale/de-DE/better-bibtex.ftl index 48954f931b..bd35c62aaa 100644 --- a/locale/de-DE/better-bibtex.ftl +++ b/locale/de-DE/better-bibtex.ftl @@ -63,8 +63,6 @@ better-bibtex_item-pane_section_sidenav = .tooltip = { -citation-key } better-bibtex_item-pane_section_header = .label = { -citation-key } -better-bibtex_item-pane_info_citation-key = - .label = { -citation-key } better-bibtex_item-pane_info_citation-key_label = { -citation-key } better-bibtex_preferences_advanced_export_brace-protection = .label = Groß-/Kleinschreibung auf Groß- und Kleinschreibung anwenden, indem sie Klammern beigefügt wird diff --git a/locale/fr-FR/better-bibtex.ftl b/locale/fr-FR/better-bibtex.ftl index 9c202cd75b..3629463cc6 100644 --- a/locale/fr-FR/better-bibtex.ftl +++ b/locale/fr-FR/better-bibtex.ftl @@ -63,8 +63,6 @@ better-bibtex_item-pane_section_sidenav = .tooltip = { -citation-key } better-bibtex_item-pane_section_header = .label = { -citation-key } -better-bibtex_item-pane_info_citation-key = - .label = { -citation-key } better-bibtex_item-pane_info_citation-key_label = { -citation-key } better-bibtex_preferences_advanced_export_brace-protection = .label = Appliquer la protection de la casse aux mots en capitales en les insérant entre paires d'accolades diff --git a/locale/it-IT/better-bibtex.ftl b/locale/it-IT/better-bibtex.ftl index 91c5a429c1..3101be86f7 100644 --- a/locale/it-IT/better-bibtex.ftl +++ b/locale/it-IT/better-bibtex.ftl @@ -63,8 +63,6 @@ better-bibtex_item-pane_section_sidenav = .tooltip = { -citation-key } better-bibtex_item-pane_section_header = .label = { -citation-key } -better-bibtex_item-pane_info_citation-key = - .label = { -citation-key } better-bibtex_item-pane_info_citation-key_label = { -citation-key } better-bibtex_preferences_advanced_export_brace-protection = .label = Includi le parole in maiuscolo tra graffe per preservare i caratteri così come scritti diff --git a/locale/pt-BR/better-bibtex.ftl b/locale/pt-BR/better-bibtex.ftl index 646aa0e8d8..a8329aad72 100644 --- a/locale/pt-BR/better-bibtex.ftl +++ b/locale/pt-BR/better-bibtex.ftl @@ -63,8 +63,6 @@ better-bibtex_item-pane_section_sidenav = .tooltip = { -citation-key } better-bibtex_item-pane_section_header = .label = { -citation-key } -better-bibtex_item-pane_info_citation-key = - .label = { -citation-key } better-bibtex_item-pane_info_citation-key_label = { -citation-key } better-bibtex_preferences_advanced_export_brace-protection = .label = Apply case-protection to capitalized words by enclosing them in braces diff --git a/locale/zh-CN/better-bibtex.ftl b/locale/zh-CN/better-bibtex.ftl index bdc4301537..ffb768380e 100644 --- a/locale/zh-CN/better-bibtex.ftl +++ b/locale/zh-CN/better-bibtex.ftl @@ -63,8 +63,6 @@ better-bibtex_item-pane_section_sidenav = .tooltip = { -citation-key } better-bibtex_item-pane_section_header = .label = { -citation-key } -better-bibtex_item-pane_info_citation-key = - .label = { -citation-key } better-bibtex_item-pane_info_citation-key_label = { -citation-key } better-bibtex_preferences_advanced_export_brace-protection = .label = 使用大括号括起首字母大写的单词以保持大小写格式 From 224db137c70dda52ee88bdb0ce2a3e38ab08a2bb Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 12:11:36 +0100 Subject: [PATCH 04/26] upgrades --- package-lock.json | 10 +++++----- package.json | 2 +- site/themes/relearn | 2 +- submodules/zotero | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 020295e241..eef33fdd1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,7 +69,7 @@ "devDependencies": { "@retorquere/bibtex-parser": "^9.0.17", "@retorquere/zotero-sync": "^1.0.27", - "@stylistic/eslint-plugin": "^2.10.1", + "@stylistic/eslint-plugin": "^2.11.0", "@types/bluebird": "^3.5.42", "@types/node": "^22.9.0", "@xmldom/xmldom": "^0.9.5", @@ -5174,13 +5174,13 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.10.1.tgz", - "integrity": "sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.11.0.tgz", + "integrity": "sha512-PNRHbydNG5EH8NK4c+izdJlxajIR6GxcUhzsYNRsn6Myep4dsZt0qFCz3rCPnkvgO5FYibDcMqgNHUT+zvjYZw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.12.2", + "@typescript-eslint/utils": "^8.13.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "estraverse": "^5.3.0", diff --git a/package.json b/package.json index ba4152b47e..df49d1b73f 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "devDependencies": { "@retorquere/bibtex-parser": "^9.0.17", "@retorquere/zotero-sync": "^1.0.27", - "@stylistic/eslint-plugin": "^2.10.1", + "@stylistic/eslint-plugin": "^2.11.0", "@types/bluebird": "^3.5.42", "@types/node": "^22.9.0", "@xmldom/xmldom": "^0.9.5", diff --git a/site/themes/relearn b/site/themes/relearn index ab4197f311..a23eb3a9d8 160000 --- a/site/themes/relearn +++ b/site/themes/relearn @@ -1 +1 @@ -Subproject commit ab4197f3113afa9a7d17bed69dbef6c0e75a4921 +Subproject commit a23eb3a9d8baa6fe6adc764815db03e70e98a381 diff --git a/submodules/zotero b/submodules/zotero index dc47650eb3..75fb301f0a 160000 --- a/submodules/zotero +++ b/submodules/zotero @@ -1 +1 @@ -Subproject commit dc47650eb353a389dd01c20102ad5f4bbca7758b +Subproject commit 75fb301f0aa90311a03810d30ed3ce3b51e5acee From 52d535603a316c5935485c736a6b953bb8a2fd06 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 12:15:06 +0100 Subject: [PATCH 05/26] contract words --- ...ilable in background export #2094.biblatex | 8 ++-- ...yshorttitle and compound words #551.bibtex | 46 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/test/fixtures/export/Make DOMParser available in background export #2094.biblatex b/test/fixtures/export/Make DOMParser available in background export #2094.biblatex index 44cb21f418..28c7ae837b 100644 --- a/test/fixtures/export/Make DOMParser available in background export #2094.biblatex +++ b/test/fixtures/export/Make DOMParser available in background export #2094.biblatex @@ -1,4 +1,4 @@ -@article{huang_DepthStudyLTE_2013, +@article{huang_IndepthStudyLTE_2013, title = {An In-Depth Study of {{LTE}}: Effect of Network Protocol and Application Behavior on Performance}, shorttitle = {An In-Depth Study of {{LTE}}}, author = {Huang, Junxian and Qian, Feng and Guo, Yihua and Zhou, Yuanyuan and Xu, Qiang and Mao, Z. Morley and Sen, Subhabrata and Spatscheck, Oliver}, @@ -35,7 +35,7 @@ This is a reference to (Atxutegi et al., 2018 \textbar Open item, \href{zotero:/ ..................................} } -@article{huang_DepthStudyLTE_2013a, +@article{huang_IndepthStudyLTE_2013a, title = {An In-Depth Study of {{LTE}}: Effect of Network Protocol and Application Behavior on Performance}, shorttitle = {An In-Depth Study of {{LTE}}}, author = {Huang, Junxian and Qian, Feng and Guo, Yihua and Zhou, Yuanyuan and Xu, Qiang and Mao, Z. Morley and Sen, Subhabrata and Spatscheck, Oliver}, @@ -72,7 +72,7 @@ This is a reference to (Atxutegi et al., 2018 \textbar Open item, \href{zotero:/ ..................................} } -@article{huang_DepthStudyLTE_2013b, +@article{huang_IndepthStudyLTE_2013b, title = {An In-Depth Study of {{LTE}}: Effect of Network Protocol and Application Behavior on Performance}, shorttitle = {An In-Depth Study of {{LTE}}}, author = {Huang, Junxian and Qian, Feng and Guo, Yihua and Zhou, Yuanyuan and Xu, Qiang and Mao, Z. Morley and Sen, Subhabrata and Spatscheck, Oliver}, @@ -95,7 +95,7 @@ We observed that LTE has significantly shorter state promotion delays and lower ..................................} } -@article{huang_DepthStudyLTE_2013c, +@article{huang_IndepthStudyLTE_2013c, title = {An In-Depth Study of {{LTE}}: Effect of Network Protocol and Application Behavior on Performance}, shorttitle = {An In-Depth Study of {{LTE}}}, author = {Huang, Junxian and Qian, Feng and Guo, Yihua and Zhou, Yuanyuan and Xu, Qiang and Mao, Z. Morley and Sen, Subhabrata and Spatscheck, Oliver}, diff --git a/test/fixtures/export/veryshorttitle and compound words #551.bibtex b/test/fixtures/export/veryshorttitle and compound words #551.bibtex index 8654d8c392..7691184a1a 100644 --- a/test/fixtures/export/veryshorttitle and compound words #551.bibtex +++ b/test/fixtures/export/veryshorttitle and compound words #551.bibtex @@ -1,11 +1,17 @@ -@book{sumner_folkways_1906, - title = {Folkways: A Study of the Social Importance of Usages, Manners, Customs, Mores, and Morals}, - shorttitle = {Folkways}, - author = {Sumner, William Graham}, - year = {1906}, - publisher = {Ginn}, - address = {Boston, MA}, - langid = {english} +@article{bauer_wars_2014, + title = {War's {{Enduring Effects}} on the {{Development}} of {{Egalitarian Motivations}} and {{In-Group Biases}}}, + author = {Bauer, Michal and Cassar, Alessandra and Chytilov{\'a}, Julie and Henrich, Joseph}, + year = {2014}, + journal = {Psychological Science}, + volume = {25}, + number = {1}, + pages = {47--57}, + issn = {0956-7976, 1467-9280}, + doi = {10.1177/0956797613493444}, + urldate = {2014-01-08}, + abstract = {In suggesting that new nations often coalesce in the decades following war, historians have posed an important psychological question: Does the experience of war generate an enduring elevation in people's egalitarian motivations toward their in-group? We administered social-choice tasks to more than 1,000 children and adults differentially affected by wars in the Republic of Georgia and Sierra Leone. We found that greater exposure to war created a lasting increase in people's egalitarian motivations toward their in-group, but not their out-groups, during a developmental window starting in middle childhood (around 7 years of age) and ending in early adulthood (around 20 years of age). Outside this window, war had no measurable impact on social motivations in young children and had only muted effects on the motivations of older adults. These ``war effects'' are broadly consistent with predictions from evolutionary approaches that emphasize the importance of group cooperation in defending against external threats, though they also highlight key areas in need of greater theoretical development.}, + langid = {english}, + pmid = {24220626} } @incollection{bowles_coevolution_2003, @@ -20,7 +26,7 @@ langid = {english} } -@article{puurtinen_group_2009, +@article{puurtinen_betweengroup_2009, title = {Between-Group Competition and Human Cooperation}, author = {Puurtinen, Mikael and Mappes, Tapio}, year = {2009}, @@ -37,18 +43,12 @@ pmid = {18826935} } -@article{bauer_wars_2014, - title = {War's {{Enduring Effects}} on the {{Development}} of {{Egalitarian Motivations}} and {{In-Group Biases}}}, - author = {Bauer, Michal and Cassar, Alessandra and Chytilov{\'a}, Julie and Henrich, Joseph}, - year = {2014}, - journal = {Psychological Science}, - volume = {25}, - number = {1}, - pages = {47--57}, - issn = {0956-7976, 1467-9280}, - doi = {10.1177/0956797613493444}, - urldate = {2014-01-08}, - abstract = {In suggesting that new nations often coalesce in the decades following war, historians have posed an important psychological question: Does the experience of war generate an enduring elevation in people's egalitarian motivations toward their in-group? We administered social-choice tasks to more than 1,000 children and adults differentially affected by wars in the Republic of Georgia and Sierra Leone. We found that greater exposure to war created a lasting increase in people's egalitarian motivations toward their in-group, but not their out-groups, during a developmental window starting in middle childhood (around 7 years of age) and ending in early adulthood (around 20 years of age). Outside this window, war had no measurable impact on social motivations in young children and had only muted effects on the motivations of older adults. These ``war effects'' are broadly consistent with predictions from evolutionary approaches that emphasize the importance of group cooperation in defending against external threats, though they also highlight key areas in need of greater theoretical development.}, - langid = {english}, - pmid = {24220626} +@book{sumner_folkways_1906, + title = {Folkways: A Study of the Social Importance of Usages, Manners, Customs, Mores, and Morals}, + shorttitle = {Folkways}, + author = {Sumner, William Graham}, + year = {1906}, + publisher = {Ginn}, + address = {Boston, MA}, + langid = {english} } From 321d56d5c0685f4cb88986a5cb97daea3f42ca18 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 12:41:57 +0100 Subject: [PATCH 06/26] show error for legacy parse From 98ff4e3363224a488ccb26cc32c58a6f4022d43a Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 13:22:45 +0100 Subject: [PATCH 07/26] show errors for legacy format --- .../Cannot change citation key formula #3058.biblatex | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/fixtures/export/Cannot change citation key formula #3058.biblatex b/test/fixtures/export/Cannot change citation key formula #3058.biblatex index e69de29bb2..68e7d7c01c 100644 --- a/test/fixtures/export/Cannot change citation key formula #3058.biblatex +++ b/test/fixtures/export/Cannot change citation key formula #3058.biblatex @@ -0,0 +1,11 @@ +@book{McDougallTJ.Barker-delete-me, + title = {Getting Started with {{TEOS-10}} and the {{Gibbs Seawater}} ({{GSW}}) {{Oceanographic Toolbox}}}, + author = {McDougall, Trevor J and Barker, Paul M}, + date = {2011}, + publisher = {Trevor J McDougall}, + location = {Battery Point, Tasmania, Australia}, + url = {https://www.teos-10.org/pubs/Getting_Started.pdf}, + isbn = {978-0-646-55621-5}, + langid = {english}, + annotation = {OCLC: 724024071} +} From 76b16f47148bdb0eda2c17049b6c2cd0524d7ac2 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 16:59:35 +0100 Subject: [PATCH 08/26] cleanup --- content/key-manager/formatter.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/content/key-manager/formatter.ts b/content/key-manager/formatter.ts index c97b20b0ac..dd138d8222 100644 --- a/content/key-manager/formatter.ts +++ b/content/key-manager/formatter.ts @@ -1425,9 +1425,10 @@ export class PatternFormatter { return this.transliterate(str).replace(allow_spaces ? this.re.unsafechars_allow_spaces : this.re.unsafechars, '').trim() } - private contract(sentences: { terms: Term[] }[]): string[] { - const terms: Term[] = [] + private split(title: string): string[] { + const contracted: Term[] = [] let tail: Term + const sentences: { terms: Term[] }[] = nlp(title).json() for (const sentence of sentences) { sentence.terms.forEach((term, i) => { if (i !== 0 && tail.post === '-') { @@ -1435,18 +1436,18 @@ export class PatternFormatter { tail.post = term.post } else { - terms.push(tail = term) + contracted.push(tail = term) } }) } - return terms.map(term => term.text).filter(term => !this.skipWords.has(term.toLowerCase())) + return contracted.map(term => term.text).filter(term => !this.skipWords.has(term.toLowerCase())) } private titleWords(title, options: { transliterate?: boolean; skipWords?: boolean; nopunct?: boolean } = {}): string[] { if (!title) return null title = title.replace(/<\/?(?:i|b|sc|nc|code|span[^>]*)>|["]/ig, '').replace(/[/:]/g, ' ') - let words = this.contract(nlp(title).json()) + let words = this.split(title) .map(word => options.nopunct ? this.nopunct(word, '') : word) .filter(word => word && !(options.skipWords && ucs2decode(word).length === 1 && !word.match(/^\d+$/) && !word.match(CJK))) From df92ae07d1cf12d8251e8df57784ed79f437bf06 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 16:59:40 +0100 Subject: [PATCH 09/26] 6.7.252 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index eef33fdd1c..d967a5828f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zotero-better-bibtex", - "version": "6.7.251", + "version": "6.7.252", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zotero-better-bibtex", - "version": "6.7.251", + "version": "6.7.252", "license": "ISC", "dependencies": { "@artsy/to-title-case": "^1.1.0", diff --git a/package.json b/package.json index df49d1b73f..f0afbfa4dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-better-bibtex", - "version": "6.7.251", + "version": "6.7.252", "description": "Make Zotero useful for us LaTeX holdouts.", "homepage": "https://retorque.re/zotero-better-bibtex", "license": "ISC", From c7137fbb322741cda67e717e600fefb55db39096 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Tue, 19 Nov 2024 17:27:38 +0100 Subject: [PATCH 10/26] 6.7.253 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d967a5828f..bf6224f00f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zotero-better-bibtex", - "version": "6.7.252", + "version": "6.7.253", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zotero-better-bibtex", - "version": "6.7.252", + "version": "6.7.253", "license": "ISC", "dependencies": { "@artsy/to-title-case": "^1.1.0", diff --git a/package.json b/package.json index f0afbfa4dc..7cb6e955f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-better-bibtex", - "version": "6.7.252", + "version": "6.7.253", "description": "Make Zotero useful for us LaTeX holdouts.", "homepage": "https://retorque.re/zotero-better-bibtex", "license": "ISC", From a3060f67a4d1037d2b9a660d02597d2574166ff5 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Wed, 20 Nov 2024 00:05:09 +0100 Subject: [PATCH 11/26] cleanup --- content/key-manager/formatter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/content/key-manager/formatter.ts b/content/key-manager/formatter.ts index 12c46da07a..9ea1da0f8c 100644 --- a/content/key-manager/formatter.ts +++ b/content/key-manager/formatter.ts @@ -418,7 +418,6 @@ export class PatternFormatter { if (formula[0] === '[') { try { formula = legacyparser.parse(formula, { reserved, items, methods }) - log.debug('legacy formula:', formula) } catch (err) { log.error(`formula-update: ${ ts } legacy-formula failed to upgrade ${ formula }: ${ err.message }`) From fe7c4ac77c6f27c9d1064b94c29bdba0992c6601 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Thu, 21 Nov 2024 15:34:13 +0100 Subject: [PATCH 12/26] Jan./year --- content/dateparser.ts | 17 +++++--- test/features/export.feature | 1 + ...ring if character is found #3067.biblatex | 0 ...s string if character is found #3067.json | 41 +++++++++++++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.biblatex create mode 100644 test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.json diff --git a/content/dateparser.ts b/content/dateparser.ts index 190854a6aa..80c211090d 100644 --- a/content/dateparser.ts +++ b/content/dateparser.ts @@ -5,6 +5,14 @@ import edtfy = require('edtfy') // import escapeStringRegexp = require('escape-string-regexp') import * as months from '../gen/dateparser-months.json' +const Month = new class { + private months = months + private re = new RegExp(Object.keys(months).sort((a, b) => b.length - a.length).map(month => `${month}[.]?`).join('|'), 'i') + + english(date: string): string { + return date.replace(this.re, (month: string) => this.months[month.toLowerCase().replace('.', '')] as string || month) + } +} import { getLocaleDateOrder } from '../submodules/zotero-utilities/date' @@ -34,8 +42,6 @@ export type ParsedDate = { approximate?: boolean } -const months_re = new RegExp(Object.keys(months).sort((a, b) => b.length - a.length).join('|'), 'i') - const Season = new class { private ranges = [ [ 13, 14, 15, 16 ], @@ -148,12 +154,11 @@ function parseEDTF(value: string): ParsedDate { catch {} try { - const edtf = normalize_edtf(EDTF.parse(edtfy(date + const edtf = normalize_edtf(EDTF.parse(edtfy(Month.english(date .normalize('NFC') .replace(/\. /, ' ') // 8. july 2011 // eslint-disable-next-line @typescript-eslint/no-unsafe-return - .replace(months_re, _ => months[_.toLowerCase()] || _) - ))) + )))) if (edtf) return edtf } catch {} @@ -329,7 +334,7 @@ export function parse(value: string, try_range = true): ParsedDate { // https://github.com/retorquere/zotero-better-bibtex/issues/868 // eslint-disable-next-line @typescript-eslint/no-unsafe-return - if (m = /^([0-9]{3,})\s([^0-9]+)(?:\s+([0-9]+))?$/.exec(value.normalize('NFC').replace(months_re, _ => months[_.toLowerCase()] || _))) { + if (m = /^([0-9]{3,})\s([^0-9]+)(?:\s+([0-9]+))?$/.exec(Month.english(value.normalize('NFC')))) { const [ , year, month, day ] = m if (months[month]) { try { diff --git a/test/features/export.feature b/test/features/export.feature index e64d5ae9e2..7fb4137afc 100644 --- a/test/features/export.feature +++ b/test/features/export.feature @@ -13,6 +13,7 @@ Feature: Export Examples: | file | references | + | Better BibLatex copied year column as string if character is found #3067 | 1 | | Cannot change citation key formula #3058 | 1 | | Citation keys are missing certain words if hyphens are used #3059 | 1 | | lastpage not work in better bitex #3050 | 1 | diff --git a/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.biblatex b/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.biblatex new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.json b/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.json new file mode 100644 index 0000000000..568ae93d31 --- /dev/null +++ b/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.json @@ -0,0 +1,41 @@ +{ + "config": { + "id": "36a3b0b5-bad0-4a04-b79b-441c7cef77db", + "label": "BetterBibTeX JSON", + "preferences": { + "kuroshiro": true + } + }, + "items": [ + { + "DOI": "10.1109/82.204108", + "ISSN": "10577130", + "accessDate": "2024-11-21T08:43:57Z", + "citationKey": "leungMultibitSigmaDelta1992", + "creators": [ + { + "creatorType": "author", + "firstName": "B.H.", + "lastName": "Leung" + }, + { + "creatorType": "author", + "firstName": "S.", + "lastName": "Sutarja" + } + ], + "date": "Jan./1992", + "issue": "1", + "itemID": 1, + "itemType": "journalArticle", + "journalAbbreviation": "IEEE Trans. Circuits Syst. II", + "libraryCatalog": "DOI.org (Crossref)", + "pages": "35-51", + "publicationTitle": "IEEE Transactions on Circuits and Systems II: Analog and Digital Signal Processing", + "rights": "https://ieeexplore.ieee.org/Xplorehelp/downloads/license-information/IEEE.html", + "title": "Multibit Sigma - Delta A/D converter incorporating a novel class of dynamic element matching techniques", + "url": "http://ieeexplore.ieee.org/document/204108/", + "volume": "39" + } + ] +} \ No newline at end of file From 3f5184cf690d6710968934eb115e74b14ebb1522 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Thu, 21 Nov 2024 22:32:05 +0100 Subject: [PATCH 13/26] always store autoJournalAbbreviation --- content/item-export-format.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/item-export-format.ts b/content/item-export-format.ts index 01c798d07e..a0ccc3fac4 100644 --- a/content/item-export-format.ts +++ b/content/item-export-format.ts @@ -69,7 +69,7 @@ export function fix(serialized: Item, item: ZoteroItem): Item { log.error(`no citation key for ${ Zotero.ItemTypes.getName(item.itemTypeID) } ${ item.id } ${ JSON.stringify(regular) }`) regular.citationKey = `temporary-citekey-${ item.id }` } - if (!regular.journalAbbreviation && typeof regular.autoJournalAbbreviation !== 'string' && Preference.autoAbbrev) { + if (typeof regular.autoJournalAbbreviation !== 'string' && Preference.autoAbbrev) { regular.autoJournalAbbreviation = JournalAbbrev.get(regular) || '' } } From f69b15e46567a6d04c147ea586c29978f24a5144 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Fri, 22 Nov 2024 13:10:54 +0100 Subject: [PATCH 14/26] soft-fail metadata fetch --- content/db/cache.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/content/db/cache.ts b/content/db/cache.ts index 2f44ca6b43..71c81cb31d 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -381,10 +381,15 @@ export const Cache = new class $Cache { private async metadata(): Promise> { const metadata: Record = {} - const tx = this.db.transaction('metadata', 'readonly') - const store = tx.objectStore('metadata') - for (const rec of (await store.getAll()) as { key: string; value: string }[]) { - metadata[rec.key] = rec.value + try { + const tx = this.db.transaction('metadata', 'readonly') + const store = tx.objectStore('metadata') + for (const rec of (await store.getAll()) as { key: string; value: string }[]) { + metadata[rec.key] = rec.value + } + } + catch (err) { + log.error(`failed to fetch metadata: ${err.message}`) } return metadata From 356ef382bbfbd5fb2487521f12b8de52adedab4f Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Fri, 22 Nov 2024 15:36:46 +0100 Subject: [PATCH 15/26] more dateparser heuristics --- content/dateparser.ts | 25 ++++++++++++------- ...ring if character is found #3067.biblatex | 14 +++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/content/dateparser.ts b/content/dateparser.ts index 80c211090d..2b170042ed 100644 --- a/content/dateparser.ts +++ b/content/dateparser.ts @@ -136,7 +136,7 @@ function swap_day_month(day: number, month: number, fix_only = false): number[] return [ day, month ] } -function parseEDTF(value: string): ParsedDate { +function parseEDTF(value: string, english: string): ParsedDate { // 2378 + 2275 let date = value @@ -154,16 +154,12 @@ function parseEDTF(value: string): ParsedDate { catch {} try { - const edtf = normalize_edtf(EDTF.parse(edtfy(Month.english(date - .normalize('NFC') - .replace(/\. /, ' ') // 8. july 2011 - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - )))) + const edtf = normalize_edtf(EDTF.parse(edtfy(english))) if (edtf) return edtf } catch {} - return { verbatim: value } + return null } export function parse(value: string, try_range = true): ParsedDate { @@ -196,6 +192,17 @@ export function parse(value: string, try_range = true): ParsedDate { if (date.type === 'date') return date } + const english = Month.english(value.normalize('NFC').replace(/[.] /, ' ')) // 8. july 2011 + + if (m = (/^([a-z]+)[-/]([0-9]+)$/i).exec(english)) { + const [ , month, year ] = m + if (months[month.toLowerCase()]) { + date = parse(`${ month } ${ year }`, false) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + if (date.type === 'date') return date + } + } + // '[origdate] date' if (try_range && (m = /^\[(.+)\]\s*(.+)$/.exec(value))) { const [ , _orig, _date ] = m @@ -330,11 +337,11 @@ export function parse(value: string, try_range = true): ParsedDate { return { type: 'date', year: parseInt(date_only), ...time_doubt } } - if (!(date = parseEDTF(value)).verbatim) return date + if (date = parseEDTF(value, english)) return date // https://github.com/retorquere/zotero-better-bibtex/issues/868 // eslint-disable-next-line @typescript-eslint/no-unsafe-return - if (m = /^([0-9]{3,})\s([^0-9]+)(?:\s+([0-9]+))?$/.exec(Month.english(value.normalize('NFC')))) { + if (m = /^([0-9]{3,})\s([^0-9]+)(?:\s+([0-9]+))?$/.exec(english)) { const [ , year, month, day ] = m if (months[month]) { try { diff --git a/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.biblatex b/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.biblatex index e69de29bb2..5c03bf472d 100644 --- a/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.biblatex +++ b/test/fixtures/export/Better BibLatex copied year column as string if character is found #3067.biblatex @@ -0,0 +1,14 @@ +@article{leungMultibitSigmaDelta1992, + title = {Multibit {{Sigma}} - {{Delta A}}/{{D}} Converter Incorporating a Novel Class of Dynamic Element Matching Techniques}, + author = {Leung, B.H. and Sutarja, S.}, + date = {1992-01}, + journaltitle = {IEEE Transactions on Circuits and Systems II: Analog and Digital Signal Processing}, + shortjournal = {IEEE Trans. Circuits Syst. II}, + volume = {39}, + number = {1}, + pages = {35--51}, + issn = {10577130}, + doi = {10.1109/82.204108}, + url = {http://ieeexplore.ieee.org/document/204108/}, + urldate = {2024-11-21} +} From 7c65aa566fa5f7092e3e6960838aa2ca71a90886 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sat, 23 Nov 2024 08:22:02 +0100 Subject: [PATCH 16/26] test for all store names --- content/db/cache.ts | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/content/db/cache.ts b/content/db/cache.ts index 71c81cb31d..11caeafbfe 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -11,7 +11,7 @@ import { bySlug } from '../../gen/translators' import version from '../../gen/version' // import { main as probe } from './cache-test' -import { CursorWithValue, Database, Transaction, Factory } from '@retorquere/indexeddb-promise' +import { ObjectStore, CursorWithValue, Database, Transaction, Factory } from '@retorquere/indexeddb-promise' import type { Translators as Translator } from '../../typings/translators' const skip = new Set([ 'keepUpdated', 'worker', 'exportFileData' ]) @@ -104,6 +104,17 @@ interface Schema extends DBSchema { export type ExportCacheName = 'BetterBibLaTeX' | 'BetterBibTeX' | 'BetterCSLJSON' | 'BetterCSLYAML' class CacheDB extends Database { + #stores = { + ZoteroSerialized: { keyPath: 'itemID' }, + touched: undefined, + metadata: { keyPath: 'key' }, + ExportContext: { keyPath: 'id', autoIncrement: true }, + BetterBibTeX: { keyPath: [ 'context', 'itemID' ]}, + BetterBibLaTeX: { keyPath: [ 'context', 'itemID' ]}, + BetterCSLJSON: { keyPath: [ 'context', 'itemID' ]}, + BetterCSLYAML: { keyPath: [ 'context', 'itemID' ]}, + } + public _upgrade(_transaction: Transaction, oldVersion: number, newVersion: number | null): void { if (typeof newVersion !== 'number') { log.info(`cache: creating ${newVersion}`) @@ -115,25 +126,23 @@ class CacheDB extends Database { this.deleteObjectStore(store) } - this.createObjectStore('ZoteroSerialized', { keyPath: 'itemID' }) - this.createObjectStore('touched') - this.createObjectStore('metadata', { keyPath: 'key' }) + const stores: Record = {} + for (const [store, options] of Object.entries(this.#stores)) { + stores[store] = this.createObjectStore(store, options) + } - const context = this.createObjectStore('ExportContext', { keyPath: 'id', autoIncrement: true }) - context.createIndex('context', 'context', { unique: true }) + stores.ExportContext.createIndex('context', 'context', { unique: true }) - const stores = [ - this.createObjectStore('BetterBibTeX', { keyPath: [ 'context', 'itemID' ]}), - this.createObjectStore('BetterBibLaTeX', { keyPath: [ 'context', 'itemID' ]}), - this.createObjectStore('BetterCSLJSON', { keyPath: [ 'context', 'itemID' ]}), - this.createObjectStore('BetterCSLYAML', { keyPath: [ 'context', 'itemID' ]}), - ] - for (const store of stores) { - store.createIndex('context', 'context') - store.createIndex('itemID', 'itemID') - store.createIndex('context-itemID', [ 'context', 'itemID' ], { unique: true }) + for (const store of ['BetterBibTeX', 'BetterBibLaTeX', 'BetterCSLJSON', 'BetterCSLYAML']) { + stores[store].createIndex('context', 'context') + stores[store].createIndex('itemID', 'itemID') + stores[store].createIndex('context-itemID', [ 'context', 'itemID' ], { unique: true }) } } + + public get complete() { + return [...this.objectStoreNames].sort().join(',') === Object.keys(this.#stores).sort().join(',') + } } class Running { @@ -409,7 +418,8 @@ export const Cache = new class $Cache { } this.db = await this.$open('open') - if (!this.db) { + if (!this.db || !this.db.complete) { + this.db?.close() log.info('cache: could not open, delete and reopen') // #2995, downgrade 6 => 7 await Factory.deleteDatabase(this.name) this.db = await this.$open('reopen') From 05712a902d1a9a06119b40636abc8ca4a0e0b80d Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sat, 23 Nov 2024 09:29:38 +0100 Subject: [PATCH 17/26] allSettled --- content/auto-export.ts | 2 +- content/db/cache.ts | 71 ++++++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/content/auto-export.ts b/content/auto-export.ts index 7fa810e113..8586d786fa 100644 --- a/content/auto-export.ts +++ b/content/auto-export.ts @@ -351,7 +351,7 @@ const queue = new class TaskQueue { } } - await Promise.all(jobs.map(job => Translators.queueJob(job))) + await Promise.allSettled(jobs.map(job => Translators.queueJob(job))) await repo.push(l10n.localize('better-bibtex_preferences_auto-export_git_message', { type: translator.label.replace('Better ', '') })) diff --git a/content/db/cache.ts b/content/db/cache.ts index 11caeafbfe..5be626bdca 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -11,7 +11,7 @@ import { bySlug } from '../../gen/translators' import version from '../../gen/version' // import { main as probe } from './cache-test' -import { ObjectStore, CursorWithValue, Database, Transaction, Factory } from '@retorquere/indexeddb-promise' +import { ObjectStore, Database, Transaction, Factory } from '@retorquere/indexeddb-promise' import type { Translators as Translator } from '../../typings/translators' const skip = new Set([ 'keepUpdated', 'worker', 'exportFileData' ]) @@ -28,6 +28,12 @@ export type ExportContext = { id: number } +async function allSettled(promises): Promise { + const settled = await Promise.allSettled(promises) + const rejected = settled.filter(result => result.status === 'rejected').length + return rejected ? `${rejected}/${promises.length}` : '' +} + export interface ExportedItemMetadata { DeclarePrefChars: string noopsort: boolean @@ -106,7 +112,6 @@ export type ExportCacheName = 'BetterBibLaTeX' | 'BetterBibTeX' | 'BetterCSLJSON class CacheDB extends Database { #stores = { ZoteroSerialized: { keyPath: 'itemID' }, - touched: undefined, metadata: { keyPath: 'key' }, ExportContext: { keyPath: 'id', autoIncrement: true }, BetterBibTeX: { keyPath: [ 'context', 'itemID' ]}, @@ -195,8 +200,9 @@ export class ExportCache { const cursor = await index.openCursor(IDBKeyRange.only(id)) if (cursor) deletes.push(store.delete(cursor.primaryKey)) } - await Promise.all(deletes) + const rejected = await allSettled(deletes) await tx.commit() + if (rejected) log.error(`cache: failed to touch ${rejected} entries in ${this.name}`) } public async clear(path: string): Promise { @@ -218,8 +224,9 @@ export class ExportCache { if (key) deletes.push(context.delete(key)) } - await Promise.all(deletes) + const rejected = await allSettled(deletes) await tx.commit() + if (rejected.length) log.error(`cache: failed to remove ${rejected} entries from ${this.name}::${path}`) } public async count(path: string): Promise { @@ -271,12 +278,15 @@ export class ExportCache { public async store(items: ExportedItem[]): Promise { const tx = this.db.transaction(this.name, 'readwrite') const store = tx.objectStore(this.name) - await Promise.all(items.map(item => store.put(item))) + const rejected = await allSettled(items.map(item => store.put(item))) await tx.commit() + if (rejected.length) log.error(`cache: failed to store ${rejected} for ${this.name}`) } } class ZoteroSerialized { + #touched = 'translators.better-bibtex.cache.touched' + public filled = 0 // exponential moving average private smoothing = 2 / (10 + 1) // keep average over last 10 fills @@ -291,11 +301,10 @@ class ZoteroSerialized { items = items.filter(item => this.cachable(item)) if (!items.length) return - let tx = this.db.transaction([ 'ZoteroSerialized', 'touched' ], 'readwrite') + let tx = this.db.transaction([ 'ZoteroSerialized' ], 'readwrite') let store = tx.objectStore('ZoteroSerialized') const cached = new Set(await store.getAllKeys()) - const touched = tx.objectStore('touched') - const purge = new Set(await touched.getAllKeys()) + const purge: Set = new Set(this.touched) const fill = items.filter(item => { if (cached.has(item.id)) { @@ -309,8 +318,10 @@ class ZoteroSerialized { return true }) - await Promise.all([ ...[...purge].map(id => store.delete(id)), touched.clear() ]) + let rejected = await allSettled([...purge].map(id => store.delete(id))) await tx.commit() + Zotero.Prefs.set(this.#touched, '') + if (rejected) log.error(`cache: failed to purge ${rejected}`) const current = (items.length - fill.length) / items.length this.filled = (current - this.filled) * this.smoothing + this.filled @@ -320,8 +331,9 @@ class ZoteroSerialized { tx = this.db.transaction(['ZoteroSerialized'], 'readwrite') store = tx.objectStore('ZoteroSerialized') const puts = serialized.map(item => store.put(item)) - await Promise.all(puts) + rejected = await allSettled(puts) await tx.commit() + if (rejected) log.error(`cache: failed to store ${rejected}`) } } @@ -342,22 +354,31 @@ class ZoteroSerialized { return items } - public async touch(ids: number[]): Promise { - const tx = this.db.transaction('touched', 'readwrite') - const store = tx.objectStore('touched') - const puts = ids.map(id => store.put(true, id)) - await Promise.all(puts) - await tx.commit() + public get touched(): number[] { + const touched = Zotero.Prefs.get(this.#touched) + if (!touched) return [] + + try { + return JSON.parse(touched) as number[] + } + catch (err) { + log.error(`cache: could not read touched: ${err.message}`) + } + return [] + } + + public touch(ids: number[]): void { + Zotero.Prefs.set(this.#touched, JSON.stringify([...(new Set([ ...this.touched, ...ids]))])) } public async purge(): Promise { - const tx = this.db.transaction([ 'ZoteroSerialized', 'touched' ], 'readwrite') + const tx = this.db.transaction([ 'ZoteroSerialized' ], 'readwrite') const serialized = tx.objectStore('ZoteroSerialized') - const touched = tx.objectStore('touched') - const purge = (await touched.getAllKeys()).map(id => serialized.delete(id)) - await Promise.all([ ...purge, touched.clear() ]) + const rejected = await allSettled(this.touched.map(id => serialized.delete(id))) await tx.commit() + if (rejected.length) log.error(`cache: failed to purge ${rejected}`) + Zotero.Prefs.set(this.#touched, '') } } @@ -474,8 +495,6 @@ export const Cache = new class $Cache { } public async touch(ids: number[]): Promise { - if (!this.available('touch')) return - if (ids.length) { for (const store of [ this.ZoteroSerialized, this.BetterBibTeX, this.BetterBibLaTeX, this.BetterCSLJSON, this.BetterCSLYAML ]) { await store.touch(ids) @@ -498,8 +517,10 @@ export const Cache = new class $Cache { const stores = [...this.db.objectStoreNames].filter(name => name !== 'metadata' && (store === '*' || name === store)) if (stores.length) { const tx = this.db.transaction(stores, 'readwrite') - await Promise.all(stores.map(name => tx.objectStore(name).clear())) + const cleared = await Promise.allSettled(stores.map(name => tx.objectStore(name).clear())) await tx.commit() + const rejected = cleared.map((result, i) => result.status === 'rejected' ? stores[i] : '').filter(_ => _).join(', ') + if (rejected) log.error(`cache: failed to clear ${rejected}`) } } @@ -537,19 +558,21 @@ export const Cache = new class $Cache { if (!this.available('dump')) return {} const tables: Record = {} - let cursor: CursorWithValue | void + // let cursor: CursorWithValue | void for (const name of this.db.objectStoreNames) { try { const tx = this.db.transaction(name, 'readonly') const store = tx.objectStore(name) switch (name) { case 'touched': + /* tables[name] = {} cursor = await store.openCursor() while (cursor) { tables[name][cursor.key as string] = cursor.value if (!await cursor.continue()) cursor = undefined } + */ break case 'metadata': From d304c3e8569fac376684018afc47135d2d8cc0c1 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sat, 23 Nov 2024 10:38:10 +0100 Subject: [PATCH 18/26] allSettled shim --- content/better-bibtex.ts | 2 + package-lock.json | 126 ++++++++++++++++++++++++++++++++++++++- package.json | 1 + 3 files changed, 128 insertions(+), 1 deletion(-) diff --git a/content/better-bibtex.ts b/content/better-bibtex.ts index d026f9d9a1..3cac94afb3 100644 --- a/content/better-bibtex.ts +++ b/content/better-bibtex.ts @@ -4,6 +4,8 @@ import flatMap from 'array.prototype.flatmap' flatMap.shim() import matchAll from 'string.prototype.matchall' matchAll.shim() +import allSettled from 'promise.allsettled' +allSettled.shim() import type Bluebird from 'bluebird' const Ready = Zotero.Promise.defer() diff --git a/package-lock.json b/package-lock.json index bf6224f00f..704319560b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "papaparse": "^5.4.1", "parse5": "^7.2.1", "pinyin": "^3.1.0", + "promise.allsettled": "^1.0.7", "punycode2": "^1.0.1", "reserved-identifiers": "^1.0.0", "safe-stable-stringify": "^2.5.0", @@ -6265,6 +6266,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.map": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.7.tgz", + "integrity": "sha512-XpcFfLoBEAhezrrNw1V+yLXkE7M6uR7xJEsxbG6c/V9v043qurwVJB9r9UTnoSioFDoz1i1VOydpWGmJpfVZbg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-array-method-boxes-properly": "^1.0.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", @@ -8093,6 +8114,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "license": "MIT" + }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -8114,6 +8141,26 @@ "node": ">= 0.4" } }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -10580,7 +10627,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -10881,6 +10927,18 @@ "dev": true, "license": "MIT" }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -11023,6 +11081,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-shared-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", @@ -11165,6 +11235,28 @@ "node": ">=0.10.0" } }, + "node_modules/iterate-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", + "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "license": "MIT", + "dependencies": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/its-set": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/its-set/-/its-set-1.2.3.tgz", @@ -16844,6 +16936,26 @@ "asap": "~2.0.3" } }, + "node_modules/promise.allsettled": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.7.tgz", + "integrity": "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==", + "license": "MIT", + "dependencies": { + "array.prototype.map": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "iterate-value": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/properties-reader": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", @@ -19017,6 +19129,18 @@ "node": ">= 0.4" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "license": "MIT", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/streamx": { "version": "2.20.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", diff --git a/package.json b/package.json index 7cb6e955f3..c8103a8ac4 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,7 @@ "papaparse": "^5.4.1", "parse5": "^7.2.1", "pinyin": "^3.1.0", + "promise.allsettled": "^1.0.7", "punycode2": "^1.0.1", "reserved-identifiers": "^1.0.0", "safe-stable-stringify": "^2.5.0", From 100b18da867f9a3b317b5d3d1daeca6ce4d0dae6 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sat, 23 Nov 2024 10:51:48 +0100 Subject: [PATCH 19/26] allsettled shim --- content/auto-export.ts | 4 +++- content/better-bibtex.ts | 2 -- content/db/cache.ts | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/content/auto-export.ts b/content/auto-export.ts index 8586d786fa..ef685d7bf2 100644 --- a/content/auto-export.ts +++ b/content/auto-export.ts @@ -25,6 +25,8 @@ import { createDB, createTable, BlinkKey } from 'blinkdb' import * as blink from '../gen/blinkdb' import { pick } from './object' +import PromiseAllSettled from 'promise.allsettled' + const cmdMeta = /(["^&|<>()%!])/ const cmdMetaOrSpace = /[\s"^&|<>()%!]/ const cmdMetaInsideQuotes = /(["%!])/ @@ -351,7 +353,7 @@ const queue = new class TaskQueue { } } - await Promise.allSettled(jobs.map(job => Translators.queueJob(job))) + await PromiseAllSettled(jobs.map(job => Translators.queueJob(job))) await repo.push(l10n.localize('better-bibtex_preferences_auto-export_git_message', { type: translator.label.replace('Better ', '') })) diff --git a/content/better-bibtex.ts b/content/better-bibtex.ts index 3cac94afb3..d026f9d9a1 100644 --- a/content/better-bibtex.ts +++ b/content/better-bibtex.ts @@ -4,8 +4,6 @@ import flatMap from 'array.prototype.flatmap' flatMap.shim() import matchAll from 'string.prototype.matchall' matchAll.shim() -import allSettled from 'promise.allsettled' -allSettled.shim() import type Bluebird from 'bluebird' const Ready = Zotero.Promise.defer() diff --git a/content/db/cache.ts b/content/db/cache.ts index 5be626bdca..6ce8656b13 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -16,6 +16,8 @@ import { ObjectStore, Database, Transaction, Factory } from '@retorquere/indexed import type { Translators as Translator } from '../../typings/translators' const skip = new Set([ 'keepUpdated', 'worker', 'exportFileData' ]) +import PromiseAllSettled from 'promise.allsettled' + export function exportContext(translator: string, displayOptions: Partial): string { const defaultOptions = bySlug[translator.replace(/ /g, '')]?.displayOptions if (!defaultOptions) throw new Error(`Unexpected translator ${ translator }`) @@ -29,7 +31,7 @@ export type ExportContext = { } async function allSettled(promises): Promise { - const settled = await Promise.allSettled(promises) + const settled = await PromiseAllSettled(promises) const rejected = settled.filter(result => result.status === 'rejected').length return rejected ? `${rejected}/${promises.length}` : '' } From 1fe6e772ffd977558ac237c13a7f3f54fe6ea63b Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sat, 23 Nov 2024 15:20:37 +0100 Subject: [PATCH 20/26] allSettled --- content/auto-export.ts | 2 +- content/db/cache.ts | 19 ++++++++++++------- content/key-manager.ts | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/content/auto-export.ts b/content/auto-export.ts index ef685d7bf2..8829504735 100644 --- a/content/auto-export.ts +++ b/content/auto-export.ts @@ -25,7 +25,7 @@ import { createDB, createTable, BlinkKey } from 'blinkdb' import * as blink from '../gen/blinkdb' import { pick } from './object' -import PromiseAllSettled from 'promise.allsettled' +import PromiseAllSettled = require('promise.allsettled') const cmdMeta = /(["^&|<>()%!])/ const cmdMetaOrSpace = /[\s"^&|<>()%!]/ diff --git a/content/db/cache.ts b/content/db/cache.ts index 6ce8656b13..60e0679be0 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -16,7 +16,7 @@ import { ObjectStore, Database, Transaction, Factory } from '@retorquere/indexed import type { Translators as Translator } from '../../typings/translators' const skip = new Set([ 'keepUpdated', 'worker', 'exportFileData' ]) -import PromiseAllSettled from 'promise.allsettled' +import PromiseAllSettled = require('promise.allsettled') export function exportContext(translator: string, displayOptions: Partial): string { const defaultOptions = bySlug[translator.replace(/ /g, '')]?.displayOptions @@ -31,9 +31,14 @@ export type ExportContext = { } async function allSettled(promises): Promise { - const settled = await PromiseAllSettled(promises) - const rejected = settled.filter(result => result.status === 'rejected').length - return rejected ? `${rejected}/${promises.length}` : '' + try { + const settled = await PromiseAllSettled(promises) + const rejected = settled.filter(result => result.status === 'rejected').length + return rejected ? `${rejected}/${promises.length}` : '' + } + catch (err) { + return err.message as string + } } export interface ExportedItemMetadata { @@ -228,7 +233,7 @@ export class ExportCache { const rejected = await allSettled(deletes) await tx.commit() - if (rejected.length) log.error(`cache: failed to remove ${rejected} entries from ${this.name}::${path}`) + if (rejected) log.error(`cache: failed to remove ${rejected} entries from ${this.name}::${path}`) } public async count(path: string): Promise { @@ -282,7 +287,7 @@ export class ExportCache { const store = tx.objectStore(this.name) const rejected = await allSettled(items.map(item => store.put(item))) await tx.commit() - if (rejected.length) log.error(`cache: failed to store ${rejected} for ${this.name}`) + if (rejected) log.error(`cache: failed to store ${rejected} for ${this.name}`) } } @@ -379,7 +384,7 @@ class ZoteroSerialized { const rejected = await allSettled(this.touched.map(id => serialized.delete(id))) await tx.commit() - if (rejected.length) log.error(`cache: failed to purge ${rejected}`) + if (rejected) log.error(`cache: failed to purge ${rejected}`) Zotero.Prefs.set(this.#touched, '') } } diff --git a/content/key-manager.ts b/content/key-manager.ts index 36019761a5..712d0c0882 100644 --- a/content/key-manager.ts +++ b/content/key-manager.ts @@ -518,7 +518,7 @@ export const KeyManager = new class _KeyManager { await Cache.touch(ids) } catch (err) { - log.error('Cache touch failed:', err) + log.error('Cache touch failed:', Object.keys(err)) } finally { void Events.emit('items-changed', { items: Zotero.Items.get(ids), action }) From d9db053ce28ee92faab5ab7adc7b553825c0db8d Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sat, 23 Nov 2024 18:15:49 +0100 Subject: [PATCH 21/26] allsettled --- content/auto-export.ts | 4 +--- content/better-bibtex.ts | 2 ++ content/db/cache.ts | 13 +++++++------ content/worker/zotero.ts | 3 +++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/content/auto-export.ts b/content/auto-export.ts index 8829504735..8586d786fa 100644 --- a/content/auto-export.ts +++ b/content/auto-export.ts @@ -25,8 +25,6 @@ import { createDB, createTable, BlinkKey } from 'blinkdb' import * as blink from '../gen/blinkdb' import { pick } from './object' -import PromiseAllSettled = require('promise.allsettled') - const cmdMeta = /(["^&|<>()%!])/ const cmdMetaOrSpace = /[\s"^&|<>()%!]/ const cmdMetaInsideQuotes = /(["%!])/ @@ -353,7 +351,7 @@ const queue = new class TaskQueue { } } - await PromiseAllSettled(jobs.map(job => Translators.queueJob(job))) + await Promise.allSettled(jobs.map(job => Translators.queueJob(job))) await repo.push(l10n.localize('better-bibtex_preferences_auto-export_git_message', { type: translator.label.replace('Better ', '') })) diff --git a/content/better-bibtex.ts b/content/better-bibtex.ts index d026f9d9a1..4f70b0a0ad 100644 --- a/content/better-bibtex.ts +++ b/content/better-bibtex.ts @@ -4,6 +4,8 @@ import flatMap from 'array.prototype.flatmap' flatMap.shim() import matchAll from 'string.prototype.matchall' matchAll.shim() +import allSettled = require('promise.allsettled') +allSettled.shim() import type Bluebird from 'bluebird' const Ready = Zotero.Promise.defer() diff --git a/content/db/cache.ts b/content/db/cache.ts index 60e0679be0..5021e78532 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -16,8 +16,6 @@ import { ObjectStore, Database, Transaction, Factory } from '@retorquere/indexed import type { Translators as Translator } from '../../typings/translators' const skip = new Set([ 'keepUpdated', 'worker', 'exportFileData' ]) -import PromiseAllSettled = require('promise.allsettled') - export function exportContext(translator: string, displayOptions: Partial): string { const defaultOptions = bySlug[translator.replace(/ /g, '')]?.displayOptions if (!defaultOptions) throw new Error(`Unexpected translator ${ translator }`) @@ -31,13 +29,16 @@ export type ExportContext = { } async function allSettled(promises): Promise { + log.debug(`cache: settling ${promises.length} promises`) + if (!promises.length) return '' + try { - const settled = await PromiseAllSettled(promises) + const settled = await Promise.allSettled(promises) const rejected = settled.filter(result => result.status === 'rejected').length - return rejected ? `${rejected}/${promises.length}` : '' + return rejected ? `${rejected}/${settled.length}` : '' } catch (err) { - return err.message as string + return `[[${err.message}]]` } } @@ -521,7 +522,7 @@ export const Cache = new class $Cache { if (!this.available('clear')) return store = store.replace(/ /g, '') - const stores = [...this.db.objectStoreNames].filter(name => name !== 'metadata' && (store === '*' || name === store)) + const stores = this.db.objectStoreNames.filter(name => name !== 'metadata' && (store === '*' || name === store)) if (stores.length) { const tx = this.db.transaction(stores, 'readwrite') const cleared = await Promise.allSettled(stores.map(name => tx.objectStore(name).clear())) diff --git a/content/worker/zotero.ts b/content/worker/zotero.ts index 296ea8d700..8ef26334e8 100644 --- a/content/worker/zotero.ts +++ b/content/worker/zotero.ts @@ -1,5 +1,8 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-assignment */ +import allSettled = require('promise.allsettled') +allSettled.shim() + import type { Attachment, Item, Note } from '../../gen/typings/serialized-item' type Serialized = Attachment | Item | Note From 5b9cd74107fff0deb737906fb61c8393027ef091 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sun, 24 Nov 2024 10:53:51 +0100 Subject: [PATCH 22/26] touched --- content/db/cache.ts | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/content/db/cache.ts b/content/db/cache.ts index 5021e78532..c37626c773 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -11,7 +11,7 @@ import { bySlug } from '../../gen/translators' import version from '../../gen/version' // import { main as probe } from './cache-test' -import { ObjectStore, Database, Transaction, Factory } from '@retorquere/indexeddb-promise' +import { CursorWithValue, ObjectStore, Database, Transaction, Factory } from '@retorquere/indexeddb-promise' import type { Translators as Translator } from '../../typings/translators' const skip = new Set([ 'keepUpdated', 'worker', 'exportFileData' ]) @@ -126,6 +126,7 @@ class CacheDB extends Database { BetterBibLaTeX: { keyPath: [ 'context', 'itemID' ]}, BetterCSLJSON: { keyPath: [ 'context', 'itemID' ]}, BetterCSLYAML: { keyPath: [ 'context', 'itemID' ]}, + touched: undefined, } public _upgrade(_transaction: Transaction, oldVersion: number, newVersion: number | null): void { @@ -293,8 +294,6 @@ export class ExportCache { } class ZoteroSerialized { - #touched = 'translators.better-bibtex.cache.touched' - public filled = 0 // exponential moving average private smoothing = 2 / (10 + 1) // keep average over last 10 fills @@ -309,10 +308,11 @@ class ZoteroSerialized { items = items.filter(item => this.cachable(item)) if (!items.length) return - let tx = this.db.transaction([ 'ZoteroSerialized' ], 'readwrite') + let tx = this.db.transaction(['ZoteroSerialized', 'touched'], 'readwrite') let store = tx.objectStore('ZoteroSerialized') const cached = new Set(await store.getAllKeys()) - const purge: Set = new Set(this.touched) + const touched = tx.objectStore('touched') + const purge = new Set(await touched.getAllKeys()) const fill = items.filter(item => { if (cached.has(item.id)) { @@ -327,8 +327,8 @@ class ZoteroSerialized { }) let rejected = await allSettled([...purge].map(id => store.delete(id))) + await touched.clear() await tx.commit() - Zotero.Prefs.set(this.#touched, '') if (rejected) log.error(`cache: failed to purge ${rejected}`) const current = (items.length - fill.length) / items.length @@ -362,31 +362,24 @@ class ZoteroSerialized { return items } - public get touched(): number[] { - const touched = Zotero.Prefs.get(this.#touched) - if (!touched) return [] - - try { - return JSON.parse(touched) as number[] - } - catch (err) { - log.error(`cache: could not read touched: ${err.message}`) - } - return [] - } - - public touch(ids: number[]): void { - Zotero.Prefs.set(this.#touched, JSON.stringify([...(new Set([ ...this.touched, ...ids]))])) + public async touch(ids: number[]): Promise { + const tx = this.db.transaction('touched', 'readwrite') + const store = tx.objectStore('touched') + const puts = ids.map(id => store.put(true, id)) + await Promise.all(puts) + await tx.commit() } public async purge(): Promise { - const tx = this.db.transaction([ 'ZoteroSerialized' ], 'readwrite') + const tx = this.db.transaction(['ZoteroSerialized', 'touched'], 'readwrite') const serialized = tx.objectStore('ZoteroSerialized') + const touched = tx.objectStore('touched') - const rejected = await allSettled(this.touched.map(id => serialized.delete(id))) + const purge = (await touched.getAllKeys()).map(id => serialized.delete(id)) + const rejected = await allSettled(purge) + await touched.clear() await tx.commit() if (rejected) log.error(`cache: failed to purge ${rejected}`) - Zotero.Prefs.set(this.#touched, '') } } @@ -566,21 +559,19 @@ export const Cache = new class $Cache { if (!this.available('dump')) return {} const tables: Record = {} - // let cursor: CursorWithValue | void + let cursor: CursorWithValue | void for (const name of this.db.objectStoreNames) { try { const tx = this.db.transaction(name, 'readonly') const store = tx.objectStore(name) switch (name) { case 'touched': - /* tables[name] = {} cursor = await store.openCursor() while (cursor) { tables[name][cursor.key as string] = cursor.value if (!await cursor.continue()) cursor = undefined } - */ break case 'metadata': From 1367f844661bbb4f08868faae56934adcc31951a Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sun, 24 Nov 2024 10:57:52 +0100 Subject: [PATCH 23/26] only is implied --- content/db/cache.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/db/cache.ts b/content/db/cache.ts index c37626c773..f14c63e4f3 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -206,7 +206,7 @@ export class ExportCache { const deletes: Promise[] = [] for (const id of ids) { - const cursor = await index.openCursor(IDBKeyRange.only(id)) + const cursor = await index.openCursor(id) if (cursor) deletes.push(store.delete(cursor.primaryKey)) } const rejected = await allSettled(deletes) @@ -224,7 +224,7 @@ export class ExportCache { const deletes: Promise[] = [] const cache = tx.objectStore(this.name as 'BetterBibTeX') - const cursor = await cache.index('context').openCursor(IDBKeyRange.only(path)) + const cursor = await cache.index('context').openCursor(path) if (cursor) deletes.push(cache.delete(cursor.primaryKey)) if (deleteContext) { @@ -242,7 +242,7 @@ export class ExportCache { const tx = this.db.transaction(this.name, 'readonly') const store = tx.objectStore(this.name) const index = store.index('context') - const count = await index.count(IDBKeyRange.only(path)) + const count = await index.count(path) return count } From 0dd3752a7857410b8c3053191060f19ca00406cb Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sun, 24 Nov 2024 11:17:04 +0100 Subject: [PATCH 24/26] log schema --- content/db/cache.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/content/db/cache.ts b/content/db/cache.ts index f14c63e4f3..98094c4aaf 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -409,6 +409,22 @@ export const Cache = new class $Cache { } } + private async schema(): Promise { + let schema = '\n' + for (const storeName of [...this.db.objectStoreNames].sort()) { + const tx = this.db.transaction(storeName, 'readonly') + const store = tx.objectStore(storeName) + schema += `Object Store: ${JSON.stringify(storeName)}, Key Path: ${JSON.stringify(store.keyPath)}\n` + + for (const indexName of [...store.indexNames].sort()) { + const index = store.index(indexName) + schema += ` Index: ${JSON.stringify(indexName)}, Key Path: ${JSON.stringify(index.keyPath)}, Unique: ${!!index.unique}\n` + } + await tx.commit() + } + return schema + } + private async metadata(): Promise> { const metadata: Record = {} @@ -447,6 +463,8 @@ export const Cache = new class $Cache { this.db = await this.$open('reopen') } + log.debug(await this.schema()) + if (!this.db) { Zotero.Prefs.set(del, true) flash('Cache could not be opened', 'Cache could not be opened, please restart Zotero') From caabfeb799b11a88e2839df068265800ae58cbd6 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sun, 24 Nov 2024 12:02:36 +0100 Subject: [PATCH 25/26] remove debug logging --- content/db/cache.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/db/cache.ts b/content/db/cache.ts index 98094c4aaf..ba5a84414a 100644 --- a/content/db/cache.ts +++ b/content/db/cache.ts @@ -29,7 +29,6 @@ export type ExportContext = { } async function allSettled(promises): Promise { - log.debug(`cache: settling ${promises.length} promises`) if (!promises.length) return '' try { @@ -463,9 +462,10 @@ export const Cache = new class $Cache { this.db = await this.$open('reopen') } - log.debug(await this.schema()) - - if (!this.db) { + if (this.db) { + log.info(await this.schema()) + } + else { Zotero.Prefs.set(del, true) flash('Cache could not be opened', 'Cache could not be opened, please restart Zotero') } From 6a4cf331bb2fbed1f53af495cb051c8b72c76ab1 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Sun, 24 Nov 2024 12:25:32 +0100 Subject: [PATCH 26/26] 6.7.254 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 704319560b..37a22d72cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zotero-better-bibtex", - "version": "6.7.253", + "version": "6.7.254", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zotero-better-bibtex", - "version": "6.7.253", + "version": "6.7.254", "license": "ISC", "dependencies": { "@artsy/to-title-case": "^1.1.0", diff --git a/package.json b/package.json index c8103a8ac4..60ebbc44fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-better-bibtex", - "version": "6.7.253", + "version": "6.7.254", "description": "Make Zotero useful for us LaTeX holdouts.", "homepage": "https://retorque.re/zotero-better-bibtex", "license": "ISC",