internetarchive / internetarchive/bookreader

Initialize br.book Before Plugins to Prevent Startup Errors

Open
#1,474 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
1.2k
Forks
491
Avg merge
2d 15h
Merged PRs (30d)
3

Description

### **Description**

BookReader can crash during initialization because plugin lifecycle work (plugin construction and/or plugin `setup()`) runs before `br.book` is created.

Currently, if a plugin (including downstream integrations’ custom plugins) touches `br.book` during its `constructor(br)` or `setup()` lifecycle, it may dereference `undefined` and throw. When this happens, BookReader fails to initialize and the UI never renders.

This is particularly noticeable in apps where book metadata or `data` may load asynchronously — the crash may appear **timing-dependent**, even if the plugin isn’t doing anything inherently wrong.

### **Symptoms**

* BookReader initialization aborts with an exception; the reader UI never appears.
* The error originates in plugin code but is caused by setup ordering (plugins run before `br.book` exists), making it hard for downstream apps to diagnose.

### **How to Reproduce**

1. Register/enable a plugin that references `br.book` during construction or setup. Minimal example:

```js
class TestPlugin extends BookReaderPlugin {
constructor(br) {
super(br);
br.book.pages; // Accessing book too early
}
}
BookReader.registerPlugin('test_plugin', TestPlugin);
```

2. Create a BookReader instance (with or without `data` loaded yet).
3. Observe the runtime error during initialization.

### **Evidence / Screenshot**

Console error example:

```
Uncaught TypeError: Cannot read properties of undefined (reading 'pages')
```

Location:

* `src/BookReader.js` — `BookReader.prototype.setup()` initialization order (plugins are initialized before `this.book` is created).

### **Expected Behavior**

* `br.book` should exist before plugin construction and before plugin `setup()` runs.
* Plugins can safely reference `br.book`, even if the underlying book `data` hasn’t loaded yet.

### **Context**

* OS: Windows 10 (win32 10.0.26100)
* Browser: (e.g., Chrome 131, Firefox 133)
* BookReader version / commit: (fill in)

### **Proposal & Constraints**

**Proposed fix:**

* Adjust initialization order in `BookReader.prototype.setup()` so `this.book` is created **before** constructing or setting up plugins.

**Implementation notes:**

* Assign `this.reduceSet`, `this.pageProgression`, `this.protected`, and `this.data` early (so `BookModel` has sane defaults).
* Create `this.book = new BookModel(this)` before plugin construction/setup.
* Keep existing plugin setup logic unchanged, but execute it after `this.book` exists.

**Constraints / Non-goals:**

* Book data does not need to be loaded; this ensures `br.book` exists during early plugin lifecycle.
* Does not attempt to make `br.book.pages` meaningful before data loads — it only prevents crashes.

**Verification:**

* Add a regression test that registers a plugin accessing `br.book.pages` during construction, then assert `new BookReader()` does **not throw**.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.