Skip to content

Commit

Permalink
Merge branch 'master' into gh-3113
Browse files Browse the repository at this point in the history
  • Loading branch information
retorquere committed Mar 2, 2025
2 parents 95bb924 + d0ba8fa commit f92c171
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 226 deletions.
11 changes: 5 additions & 6 deletions content/ErrorReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ type Wizard = HTMLElement & {
rewind: () => void
}

import { version as running } from '../gen/version.json'

type Report = {
context: string
errors: string
Expand Down Expand Up @@ -77,8 +79,6 @@ export class ErrorReport {
wizard.getButton('cancel').disabled = true
wizard.canRewind = false

const version = require('../gen/version.js')

try {
await Zotero.HTTP.request('PUT', `${ this.bucket }/${ this.zipfile() }`, {
noCache: true,
Expand All @@ -96,7 +96,7 @@ export class ErrorReport {
wizard.advance();

// eslint-disable-next-line no-magic-numbers
(<HTMLInputElement> this.document.getElementById('better-bibtex-report-id')).value = `${ this.name() }/${version}`
(<HTMLInputElement> this.document.getElementById('better-bibtex-report-id')).value = `${ this.name() }/${running}`
this.document.getElementById('better-bibtex-report-result').hidden = false
}
catch (err) {
Expand Down Expand Up @@ -359,14 +359,13 @@ export class ErrorReport {

await this.reload()

const current = require('../gen/version.js')
this.setValue('better-bibtex-report-current', l10n.localize('better-bibtex_error-report_better-bibtex_current', { version: current }))
this.setValue('better-bibtex-report-current', l10n.localize('better-bibtex_error-report_better-bibtex_current', { version: running }))

try {
const latest = await this.latest()

const show_latest = <HTMLInputElement> this.document.getElementById('better-bibtex-report-latest')
if (current === latest) {
if (running === latest) {
show_latest.hidden = true
}
else {
Expand Down
2 changes: 1 addition & 1 deletion content/aux-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { findBinary } from './path-search'
import { log } from './logger'
import { alert } from './prompt'

const version = require('../gen/version.js')
import { version } from '../gen/version.json'

type Source = 'MarkDown' | 'BibTeX AUX'

Expand Down
2 changes: 1 addition & 1 deletion content/db/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if (!worker && typeof IDBKeyRange === 'undefined') IDBKeyRange = Components.clas

import type { Serialized, Serializer } from '../item-export-format'
import { bySlug } from '../../gen/translators'
import version from '../../gen/version'
import { version } from '../../gen/version.json'
// import { main as probe } from './cache-test'

import type { Translators as Translator } from '../../typings/translators'
Expand Down
4 changes: 3 additions & 1 deletion content/json-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { Preference } from './prefs'
import { orchestrator } from './orchestrator'
import { Server } from './server'

import { version as BBTVersion } from '../gen/version.json'

import { methods } from '../gen/api/json-rpc'

const OK = 200
Expand Down Expand Up @@ -621,7 +623,7 @@ export class NSAPI {
* Returns the Zotero and BetterBibTeX version to show the JSON-RPC API is ready.
*/
public async ready(): Promise<{ zotero: string; betterbibtex: string }> {
return { zotero: Zotero.version, betterbibtex: require('../gen/version.js') }
return { zotero: Zotero.version, betterbibtex: BBTVersion }
}
}

Expand Down
2 changes: 1 addition & 1 deletion content/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
declare const Zotero: any

import * as client from './client'
const version = require('./../gen/version.js')
import { version } from './../gen/version.json'
export const run = `<${version} ${client.run}>`

declare const dump: (msg: string) => void
Expand Down
243 changes: 82 additions & 161 deletions content/translators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Cache } from './db/cache'
import { Serializer } from './item-export-format'

declare class ChromeWorker extends Worker { }
import PromiseWorker from '@kotorik/promise-worker'

Components.utils.import('resource://zotero/config.js')
declare const ZOTERO_CONFIG: any
Expand All @@ -14,9 +15,7 @@ import type { Translators as Translator } from '../typings/translators'
import { Preference } from './prefs'
import { affects, Preferences } from '../gen/preferences/meta'
import { log } from './logger'
import { flash } from './flash'
import { Events } from './events'
import { Pinger } from './ping'
import { newQueue } from '@henrygd/queue'
import { orchestrator } from './orchestrator'
import type { Reason } from './bootstrap'
Expand Down Expand Up @@ -59,7 +58,8 @@ export const Translators = new class { // eslint-disable-line @typescript-eslint
public byLabel: Record<string, Translator.Header> = {}
public bySlug: Record<string, Translator.Header> = {}
public queue = newQueue(1)
public worker: ChromeWorker
#worker: ChromeWorker
public worker: PromiseWorker<Translator.Worker.Message, Translator.Worker.Message>

private reinit: { header: Translator.Header; code: string }[]
private serializer = new Serializer
Expand All @@ -74,32 +74,34 @@ export const Translators = new class { // eslint-disable-line @typescript-eslint
description: 'translators',
needs: [ 'keymanager' ],
startup: async () => {
if (!this.worker) {
try {
const searchParams = Object.entries(client)
.map(([ k, v ]) => `${ encodeURIComponent(k) }=${ encodeURIComponent(`${v}`) }`).join('&')
const url = new URL('chrome://zotero-better-bibtex/content/worker/zotero.js')
const params = new URLSearchParams(client as unknown as Record<string, string>)
url.search = params.toString()
this.#worker = new ChromeWorker(url.toString())
this.worker = new PromiseWorker(this.#worker)

// post dynamically to fix #2485
await this.worker.postMessage({
kind: 'initialize',
CSL_MAPPINGS: Object.entries(Zotero.Schema).reduce((acc, [ k, v ]) => { if (k.startsWith('CSL')) acc[k] = v; return acc }, {}),
dateFormatsJSON: Zotero.File.getResource('resource://zotero/schema/dateFormats.json'),
})

this.worker = new ChromeWorker(`chrome://zotero-better-bibtex/content/worker/zotero.js?${ searchParams }`)
this.#worker.addEventListener('message', (e: MessageEvent) => {
const data = (e.data || []) as Translator.Worker.Message | string[]
if (Array.isArray(data)) return

// post dynamically to fix #2485
this.worker.postMessage({
kind: 'initialize',
CSL_MAPPINGS: Object.entries(Zotero.Schema).reduce((acc, [ k, v ]) => { if (k.startsWith('CSL')) acc[k] = v; return acc }, {}),
dateFormatsJSON: Zotero.File.getResource('resource://zotero/schema/dateFormats.json'),
})
}
catch (err) {
log.error('translate: worker not acquired', err)
if (Preference.testing) throw err

flash(
'Failed to start background export',
`Could not start background export (${ err.message }). Background exports have been disabled until restart -- report this as a bug at the Better BibTeX github project`,
15
)
this.worker = null
switch (data.kind) {
case 'debug':
// this is pre-formatted
Zotero.debug(e.data.message) // eslint-disable-line no-restricted-syntax
break

case 'progress':
void Events.emit('export-progress', { pct: e.data.percent, message: e.data.translator, ae: e.data.autoExport })
break
}
}
})

// cleanup old translators
this.uninstall('Better BibTeX Quick Copy')
Expand All @@ -111,6 +113,8 @@ export const Translators = new class { // eslint-disable-line @typescript-eslint
ready.resolve(true as unknown as void)
},
shutdown: async (reason: Reason) => {
this.#worker.terminate()

switch (reason) {
case 'ADDON_DISABLE':
case 'ADDON_UNINSTALL':
Expand Down Expand Up @@ -212,155 +216,72 @@ export const Translators = new class { // eslint-disable-line @typescript-eslint

const translator = this.byId[job.translatorID]

const start = Date.now()

const preferences = job.preferences || {}

const deferred = Zotero.Promise.defer()

try {
let failed = true

const config: Translator.Worker.Job = {
preferences: { ...Preference.all, ...preferences },
options: displayOptions,
data: {
items: [],
collections: [],
},
autoExport: job.autoExport,

translator: translator.label,
output: job.path || '',
debugEnabled: !!Zotero.Debug.storing,
}

let items: any[] = []
this.worker.onmessage = (e: { data: Translator.Worker.Message }) => {
switch (e.data?.kind) {
case 'error':
log.error(`translation failed: ${ e.data.message }\n${ e.data.stack || '' }`.trim())
if (job.translate) {
// job.translate._runHandler('error', e.data) // eslint-disable-line no-underscore-dangle
job.translate.complete(false, { message: e.data.message, stack: e.data.stack })
}
deferred.reject(new Error(e.data.message) as unknown as void)
break

case 'debug':
// this is pre-formatted
Zotero.debug(e.data.message) // eslint-disable-line no-restricted-syntax
break

case 'item':
job.translate?._runHandler('itemDone', items[e.data.item]) // eslint-disable-line no-underscore-dangle
break

case 'done':
void Events.emit('export-progress', { pct: 100, message: translator.label, ae: job.autoExport })
if (job.translate) {
job.translate.string = e.data.output // eslint-disable-line id-blacklist
job.translate.complete(e.data.output)
}
deferred.resolve(e.data.output as unknown as void)
failed = false
break
const config: Translator.Worker.Job = {
preferences: { ...Preference.all, ...preferences },
options: displayOptions,
data: {
items: [],
collections: [],
},
autoExport: job.autoExport,

case 'progress':
void Events.emit('export-progress', { pct: e.data.percent, message: e.data.translator, ae: e.data.autoExport })
break
translator: translator.label,
output: job.path || '',
debugEnabled: !!Zotero.Debug.storing,
}

default:
if (JSON.stringify(e) !== '{"isTrusted":true}') { // why are we getting this?
log.error('unexpected message from worker', e)
}
break
}
}
let items: any[] = []
const scope = this.exportScope(job.scope)
let collections: any[] = []
switch (scope.type) {
case 'library':
items = await Zotero.Items.getAll(scope.id, true)
collections = Zotero.Collections.getByLibrary(scope.id) // , true)
break

this.worker.onerror = e => {
log.error('QBW: failed:', Date.now() - start, 'message:', e)
// job.translate?._runHandler('error', e) // eslint-disable-line no-underscore-dangle
job.translate?.complete(false, { message: e.message, stack: e.error?.stack })
deferred.reject(new Error(e.message) as unknown as void)
}
case 'items':
items = scope.items
break

const scope = this.exportScope(job.scope)
let collections: any[] = []
switch (scope.type) {
case 'library':
items = await Zotero.Items.getAll(scope.id, true)
collections = Zotero.Collections.getByLibrary(scope.id) // , true)
break

case 'items':
items = scope.items
break

case 'collection':
collections = Zotero.Collections.getByParent(scope.collection.id, true)
const items_with_duplicates = new Set(scope.collection.getChildItems())
for (const collection of collections) {
for (const item of collection.getChildItems()) {
items_with_duplicates.add(item) // sure hope getChildItems doesn't return a new object?!
}
case 'collection':
collections = Zotero.Collections.getByParent(scope.collection.id, true)
const items_with_duplicates = new Set(scope.collection.getChildItems())
for (const collection of collections) {
for (const item of collection.getChildItems()) {
items_with_duplicates.add(item) // sure hope getChildItems doesn't return a new object?!
}
items = Array.from(items_with_duplicates.values())
break

default:
throw new Error(`Unexpected scope: ${ Object.keys(scope) }`)
}
if (job.path && job.canceled) return ''

items = items.filter(item => !item.isAnnotation?.())

const prepare = new Pinger({
total: items.length,
callback: pct => {
const pending = this.queue.size() - 1
const preparing = pending
? l10n.localize('better-bibtex_preferences_auto-export_status_preparing_delayed', { translator: translator.label, pending })
: l10n.localize('better-bibtex_preferences_auto-export_status_preparing', { translator: translator.label })
void Events.emit('export-progress', { pct, message: preparing, ae: job.autoExport })
},
})

await Cache.ZoteroSerialized.fill(items, this.serializer)
}
items = Array.from(items_with_duplicates.values())
break

config.data.items = items.map(item => item.id)
prepare.update()
if (job.path && job.canceled) return ''
default:
throw new Error(`Unexpected scope: ${ Object.keys(scope) }`)
}
if (job.path && job.canceled) return ''

if (this.byId[job.translatorID].configOptions?.getCollections) {
config.data.collections = collections.map(collection => {
collection = collection.serialize(true)
collection.id = collection.primary.collectionID
collection.name = collection.fields.name
return collection
})
}
items = items.filter(item => !item.isAnnotation?.())

prepare.done()
await Cache.ZoteroSerialized.fill(items, this.serializer)

this.worker.postMessage({ kind: 'start', config })
config.data.items = items.map(item => item.id)
if (job.path && job.canceled) return ''

if (typeof job.timeout === 'number') {
Zotero.Promise.delay(job.timeout * 1000).then(() => {
if (failed) {
const err = new TimeoutError(`translation timeout after ${ job.timeout } seconds`, { timeout: job.timeout })
log.error('translation.exportItems:', err)
deferred.reject(err as unknown as void)
}
})
}
}
catch (err) {
job.translate?.complete(false, err)
deferred.reject(err)
if (this.byId[job.translatorID].configOptions?.getCollections) {
config.data.collections = collections.map(collection => {
collection = collection.serialize(true)
collection.id = collection.primary.collectionID
collection.name = collection.fields.name
return collection
})
}

return deferred.promise as unknown as Promise<string>
const result = await this.worker.postMessage({ kind: 'start', config })
switch (result.kind) {
case 'done': return result.output
default: throw new Error(`Unexpected message of type ${result.kind}`)
}
}

public displayOptions(translatorID: string, displayOptions: any): any {
Expand Down
Loading

0 comments on commit f92c171

Please sign in to comment.