texlite 0.7.7 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +460 -0
- package/NPM_TESTING.md +130 -0
- package/OPERATIONS.md +246 -0
- package/README.md +78 -306
- package/README.zh-CN.md +61 -330
- package/dist/client/assets/{GitDialog-Bm5yMpQK.js → GitDialog-DdLrbz_H.js} +1 -1
- package/dist/client/assets/{HistoryDialog-Po4KHT_2.js → HistoryDialog-AlF9iozX.js} +1 -1
- package/dist/client/assets/{LatexEditor-CnKCXaHr.js → LatexEditor-DH9v1wgS.js} +22 -22
- package/dist/client/assets/{PdfPreview-Cd20VIjT.js → PdfPreview-CanYnyfJ.js} +1 -1
- package/dist/client/assets/ProjectNavigationDialogs-DiXtkTj1.js +1 -0
- package/dist/client/assets/{SystemMetricsDialog-DdOlcqqK.js → SystemMetricsDialog-DimBI7er.js} +1 -1
- package/dist/client/assets/index-CdiutUgf.css +1 -0
- package/dist/client/assets/index-vpWlpUOr.js +64 -0
- package/dist/client/assets/{minus-O6pK-6mA.js → minus-DR-ttrx3.js} +1 -1
- package/dist/client/assets/spellCheck-Wn7_eF9j.js +1 -0
- package/dist/client/index.html +2 -2
- package/dist/server/app.js +3 -3
- package/dist/server/compiler.js +139 -10
- package/dist/server/harper.js +210 -119
- package/dist/server/latexSpellMask.js +257 -0
- package/dist/server/projects.js +0 -12
- package/dist/server/routes/compile.js +54 -41
- package/dist/server/routes/projectFiles.js +11 -0
- package/dist/server/routes/projectShared.js +1 -1
- package/dist/server/routes/projects.js +20 -19
- package/package.json +4 -2
- package/dist/client/assets/ProjectNavigationDialogs-BDOtIoTy.js +0 -1
- package/dist/client/assets/index-MDwr9W9R.css +0 -1
- package/dist/client/assets/index-zIjCeZFK.js +0 -64
- package/dist/client/assets/spellCheck-Ce3o_xO-.js +0 -4
package/DESIGN.md
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# TexLite design
|
|
2
|
+
|
|
3
|
+
This document records the design goals and the implementation choices that
|
|
4
|
+
shape TexLite. It complements the short [README](README.md) and the
|
|
5
|
+
[operations guide](OPERATIONS.md), which covers installation, configuration,
|
|
6
|
+
and day-to-day operation.
|
|
7
|
+
|
|
8
|
+
## Design goals
|
|
9
|
+
|
|
10
|
+
- Use the host's TeX Live/LaTeX installation so it can be updated independently.
|
|
11
|
+
- Keep the deployment small: one Node.js process, SQLite, local files, and no
|
|
12
|
+
Redis, MongoDB, reverse proxy, or bundled LaTeX image for the default
|
|
13
|
+
localhost setup.
|
|
14
|
+
- Keep project source, compile output, history, and credentials under one
|
|
15
|
+
configurable data directory so that a complete backup is straightforward.
|
|
16
|
+
- Provide useful real-time collaboration for a small trusted group rather than
|
|
17
|
+
emulate a distributed Overleaf deployment.
|
|
18
|
+
- Prefer durable, explicit boundaries over silently accepting a possibly stale
|
|
19
|
+
or mixed-time source tree.
|
|
20
|
+
|
|
21
|
+
## Architecture
|
|
22
|
+
|
|
23
|
+
| Area | Implementation |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| Browser UI | React, Vite, CodeMirror, PDF.js |
|
|
26
|
+
| Localization | Frontend JSON resources plus a server-side error-code catalog, selected from `Accept-Language` |
|
|
27
|
+
| Editor language features | CodeMirror LaTeX syntax/folding, auto-pairs, project completion index, optional Vim mode |
|
|
28
|
+
| Writing assistance | Optional host `harper-cli`, with a linear server-side LaTeX mask and native browser spellcheck fallback; project dictionary stored by the server |
|
|
29
|
+
| API and static server | Fastify, WebSocket |
|
|
30
|
+
| Collaboration | Yjs, y-websocket, awareness messages, y-codemirror.next |
|
|
31
|
+
| Database | SQLite through better-sqlite3, foreign keys and WAL mode |
|
|
32
|
+
| Files | Local project directories under the configured data directory |
|
|
33
|
+
| Compilation | Host `latexmk` and a configured LaTeX engine |
|
|
34
|
+
| Formatting | Browser-side `tex-fmt` WASM package and `bibtex-tidy` |
|
|
35
|
+
| Git backup | Optional host Git and the GitHub REST API |
|
|
36
|
+
| Process management | Foreground `serve`, or the bundled PM2 lifecycle commands |
|
|
37
|
+
|
|
38
|
+
## Localization
|
|
39
|
+
|
|
40
|
+
The browser owns interface copy through the English and Chinese JSON resources
|
|
41
|
+
in `src/client/locales`. API errors use stable codes rather than route-local
|
|
42
|
+
human text. The server resolves those codes through `src/server/i18n.ts` using
|
|
43
|
+
the request's `Accept-Language` header; the browser API helper always supplies
|
|
44
|
+
its active language, and unmatched or non-browser requests fall back to
|
|
45
|
+
English. This keeps direct API clients usable while allowing the React client
|
|
46
|
+
to retain its more contextual local translations.
|
|
47
|
+
|
|
48
|
+
New user-correctable server failures must use `apiError()` for an immediate
|
|
49
|
+
response or `httpError()` for a thrown response. Do not place translated text
|
|
50
|
+
in route, filesystem, Git, or compiler-control code. Operational logs and
|
|
51
|
+
raw LaTeX output remain in their original form, so they can be searched and
|
|
52
|
+
diagnosed without changing behavior by browser language.
|
|
53
|
+
|
|
54
|
+
### Citation library
|
|
55
|
+
|
|
56
|
+
The citation library is stored in SQLite and is independent of project source
|
|
57
|
+
files. A `.bib` editor tab parses complete BibTeX entries locally, so saving a
|
|
58
|
+
reference preserves the author's original formatting; importing inserts the
|
|
59
|
+
selected entry at the current editor cursor and requires project write access.
|
|
60
|
+
Each user owns a private library. Citation entries and color tags are scoped to
|
|
61
|
+
the owning user; other users cannot list, search, import, edit, or delete them.
|
|
62
|
+
The homepage keeps Projects as the default view and exposes the
|
|
63
|
+
citation library as a separate management page beside the other management
|
|
64
|
+
controls; the project workspace keeps a smaller `.bib` import dialog for
|
|
65
|
+
in-context writing. Library results are server-filtered and paginated with 60
|
|
66
|
+
entries per page by default (the API accepts a bounded page-size override).
|
|
67
|
+
Library entries are checked in the browser with the `bibtex-tidy` JavaScript
|
|
68
|
+
parser and formatter before a mutation request is sent. Citation keys are unique
|
|
69
|
+
per user without regard to letter case. Creating an existing key is rejected;
|
|
70
|
+
updating an entry or its tags requires the revision last read by the client, so
|
|
71
|
+
concurrent browser sessions report a conflict instead of silently overwriting
|
|
72
|
+
newer content. Entry-text and tag-only mutations use separate endpoints, while
|
|
73
|
+
the explicit save-from-`.bib` action can replace the stored entry without
|
|
74
|
+
discarding its existing tags. The server retains only transport-level size and
|
|
75
|
+
field-shape limits; it does not reparse BibTeX syntax.
|
|
76
|
+
|
|
77
|
+
TexLite is intentionally a single-instance application. The collaboration
|
|
78
|
+
rooms, project mutation queues, compile coordinator, and SQLite database are
|
|
79
|
+
process-local. Startup acquires an atomic `.texlite.lock` directory in the data
|
|
80
|
+
directory and installs its owner record atomically; a live second process is
|
|
81
|
+
rejected, while a stale lock from a dead process can be recovered without
|
|
82
|
+
deleting a lock that is still being initialized. Cluster mode and multiple
|
|
83
|
+
application processes sharing one data directory are not supported.
|
|
84
|
+
|
|
85
|
+
## Configuration and startup
|
|
86
|
+
|
|
87
|
+
The npm package keeps configuration outside the package installation. The
|
|
88
|
+
effective configuration is selected in this order:
|
|
89
|
+
|
|
90
|
+
1. `--config PATH`;
|
|
91
|
+
2. `TEXLITE_CONFIG`;
|
|
92
|
+
3. `$XDG_CONFIG_HOME/texlite/texlite.config.json`;
|
|
93
|
+
4. `~/.config/texlite/texlite.config.json`.
|
|
94
|
+
|
|
95
|
+
The data directory defaults to `$XDG_DATA_HOME/texlite` or
|
|
96
|
+
`~/.local/share/texlite`, and can be changed with `storage.dataDir` or
|
|
97
|
+
`TEXLITE_DATA_DIR`. Relative configured paths are resolved relative to the
|
|
98
|
+
configuration file. The effective defaults and accepted ranges are documented
|
|
99
|
+
in the [operations guide](OPERATIONS.md) and are also available through
|
|
100
|
+
`texlite config`.
|
|
101
|
+
|
|
102
|
+
`texlite init` creates the configuration when necessary and creates the first
|
|
103
|
+
administrator. The server refuses to start without at least one active
|
|
104
|
+
administrator; public registration is not enabled. Configuration values are
|
|
105
|
+
validated before environment checks, database opening, or binding the HTTP
|
|
106
|
+
listener. Validation covers paths, limits, engines, timeout/queue settings,
|
|
107
|
+
URLs, and cross-field constraints such as the default engine being present in
|
|
108
|
+
the allowed-engine list.
|
|
109
|
+
|
|
110
|
+
Startup and `doctor` check `latexmk` and every configured LaTeX engine. Git is
|
|
111
|
+
optional: a host without Git can run the editor and compiler, while Git is
|
|
112
|
+
checked on demand when an owner opens or uses Git integration (or explicitly
|
|
113
|
+
with `texlite doctor --git`). Formatting is also optional: the browser loads
|
|
114
|
+
the bundled npm `tex-fmt` WASM module and `bibtex-tidy` only when formatting is
|
|
115
|
+
requested. TexLite never installs or updates TeX packages.
|
|
116
|
+
|
|
117
|
+
`texlite serve` runs in the foreground and is suitable for debugging, Docker,
|
|
118
|
+
or systemd. `start`, `status`, `stop`, `restart`, and `logs` use the bundled
|
|
119
|
+
PM2 dependency. Managed startup waits for both PM2 and the HTTP health probe;
|
|
120
|
+
status has a colored systemctl-style view and a `--json` form for scripts.
|
|
121
|
+
PM2 7.0.3 currently declares `js-yaml@4.3.0`, so dependency audits may report
|
|
122
|
+
the upstream GHSA-5p4m-2wfm-xmqj advisory. TexLite invokes PM2 through its
|
|
123
|
+
JavaScript API and does not load user- or project-supplied YAML configuration;
|
|
124
|
+
deployments requiring a zero-advisory dependency tree can instead supervise
|
|
125
|
+
`texlite serve` with systemd or Docker until PM2 updates that dependency.
|
|
126
|
+
Expired login sessions are pruned at startup and periodically while the
|
|
127
|
+
process is running, rather than merely being ignored during authentication.
|
|
128
|
+
Administrators can open the System status view (or authenticated
|
|
129
|
+
`GET /api/health/metrics`) for in-memory uptime, resource, queue, collaboration,
|
|
130
|
+
event-loop, and recent latency summaries. These metrics intentionally exclude
|
|
131
|
+
source text, passwords, tokens, and comment content.
|
|
132
|
+
|
|
133
|
+
## Collaboration and source persistence
|
|
134
|
+
|
|
135
|
+
Each open project has one Yjs room. Awareness data provides active-session
|
|
136
|
+
avatars, user names, permissions, file paths, cursor colors, and the ten-session
|
|
137
|
+
project limit. Ordinary Yjs edits remain concurrent; source-tree replacement
|
|
138
|
+
operations temporarily enter maintenance, notify/close collaborators, and
|
|
139
|
+
rotate the collaboration epoch so an old offline draft cannot overwrite a
|
|
140
|
+
checkout or history restore. Browser IndexedDB retains unsent updates across a
|
|
141
|
+
transient disconnect.
|
|
142
|
+
|
|
143
|
+
Read permission is intentionally different from edit permission: read-only
|
|
144
|
+
members cannot modify source files, but can view the project and add or reply
|
|
145
|
+
to source comments. Comments are anchored to source offsets and selected text,
|
|
146
|
+
can be resolved, edited, deleted, and replied to. If an author is removed,
|
|
147
|
+
the record remains visible as “Deleted User”.
|
|
148
|
+
|
|
149
|
+
The collaboration service uses a versioned handshake and a versioned epoch
|
|
150
|
+
marker. When a browser from an incompatible release connects, it is forced to
|
|
151
|
+
reload before it can decode or send source updates. Protocol-only migrations
|
|
152
|
+
preserve the browser's offline draft; source-tree replacements still clear the
|
|
153
|
+
draft because the server tree is authoritative.
|
|
154
|
+
|
|
155
|
+
The collaboration service persists dirty text with atomic temporary-file writes
|
|
156
|
+
and returns a receipt containing `revision`, `persistedAt`, `ok`, and failed
|
|
157
|
+
paths. Failed writes remain dirty and are retried in the background. Every
|
|
158
|
+
ordinary source operation must accept a successful receipt: the coordinator
|
|
159
|
+
returns `409 SOURCE_FLUSH_FAILED` with `failedPaths` instead of proceeding with
|
|
160
|
+
a stale disk tree. This applies to Git checkout/restore, history restore,
|
|
161
|
+
file/folder writes, moves/deletes, project deletion, and consistent reads. A
|
|
162
|
+
flush requested while a snapshot barrier is active is deferred until the
|
|
163
|
+
barrier closes, so the browser does not receive a false failure for an edit that
|
|
164
|
+
was intentionally held in memory. If a collaborative text edit exceeds the
|
|
165
|
+
configured limit, it is restored to the last durable content and the flush
|
|
166
|
+
receipt identifies the rejected path.
|
|
167
|
+
|
|
168
|
+
Yjs state persistence is reserved for source and HTTP-originated text updates.
|
|
169
|
+
Ephemeral metadata such as compile status, file-list revisions, and comment or
|
|
170
|
+
dictionary invalidation markers is broadcast live and reconstructed from the
|
|
171
|
+
database/source tree after restart; metadata-only events therefore do not cause
|
|
172
|
+
another full synchronous Yjs state rewrite. During room recovery the server
|
|
173
|
+
removes old markers and validates any queued/running compile state against
|
|
174
|
+
`compile_runs`. The collaboration handshake also sends a small
|
|
175
|
+
server-authoritative compile-state snapshot, so a stale browser IndexedDB entry
|
|
176
|
+
cannot make a read-only workspace appear permanently busy.
|
|
177
|
+
|
|
178
|
+
Each collaboration message refreshes the account record before applying an
|
|
179
|
+
update. Disabling a user, deleting the account, or changing project membership
|
|
180
|
+
therefore takes effect for an already-open socket rather than relying only on
|
|
181
|
+
the next reconnect. Binary uploads also publish a source-tree event so other
|
|
182
|
+
open workspaces refresh their file lists even though binary files are not Yjs
|
|
183
|
+
text objects.
|
|
184
|
+
|
|
185
|
+
`ProjectMutationCoordinator` has two related controls:
|
|
186
|
+
|
|
187
|
+
| Operation class | Examples | Coordination behavior |
|
|
188
|
+
| --- | --- | --- |
|
|
189
|
+
| Serialized source operation | File writes, settings, Git metadata, project-wide replace | Waits for queued operations, waits for a ready room, flushes the room, then runs while holding the per-project queue. |
|
|
190
|
+
| Exclusive source replacement | Git checkout/restore, history restore, project deletion, path move | Waits for active compilation, flushes successfully, enters maintenance, performs the replacement, and resets the collaboration epoch. |
|
|
191
|
+
| Consistent source read | Archive/download, raw source, history comparison, search, outline, completion index, Git diff/history | Uses the queue and a short snapshot barrier. The barrier blocks disk autosaves while an asynchronous scan/copy runs, then validates the deferred flush before returning. |
|
|
192
|
+
| Background compilation | `latexmk` on an immutable snapshot | Holds a compile reservation but not the ordinary source queue, so editing, source reads, and the retained PDF can continue. |
|
|
193
|
+
| Compile-state cleanup | Cache/artifact recovery | Waits for active compiles and serializes output removal without disconnecting collaborators. |
|
|
194
|
+
| Published PDF read | PDF.js/range requests | Reads an immutable published bundle directly and does not wait for cold Yjs-room initialization; a concurrent cleanup may produce a normal 404. |
|
|
195
|
+
| Cold file-list read | Project file tree | Uses the queue and a short barrier, but does not wait for a cold Yjs-room hydration; a concurrent cleanup is treated as a normal missing entry. |
|
|
196
|
+
|
|
197
|
+
The source tree is checked with `lstat`-based path walks. ZIP imports, project
|
|
198
|
+
duplication, Git checkout, file listing, and source resolution reject symbolic
|
|
199
|
+
links (except that deletion may address a final link itself without following
|
|
200
|
+
its target). This prevents a project path from escaping its source directory.
|
|
201
|
+
|
|
202
|
+
## Editor, files, and navigation
|
|
203
|
+
|
|
204
|
+
The editor is CodeMirror-based and provides LaTeX syntax highlighting, folding,
|
|
205
|
+
auto-pairs (including `\begin{...}`/`\end{...}`), indentation, Vim mode when
|
|
206
|
+
explicitly enabled, and completion items from built-in LaTeX plus project
|
|
207
|
+
`.tex`, `.sty`, and `.cls` definitions and BibTeX labels. Completion indexes and
|
|
208
|
+
outlines are metadata-keyed and coalesced; a source-tree change invalidates the
|
|
209
|
+
corresponding cache.
|
|
210
|
+
|
|
211
|
+
Opening `.tex`, `.bib`, `.sty`, or `.cls` files in editor tabs is an optional
|
|
212
|
+
per-user/per-project preference and is off by default. The active tab is
|
|
213
|
+
highlighted and keyboard accessible. PDF/SyncTeX synchronization is available
|
|
214
|
+
only for the current `.tex` root document; a non-root tab remains editable but
|
|
215
|
+
does not claim a PDF location.
|
|
216
|
+
|
|
217
|
+
Each project collaboration object owns one Yjs undo manager per source file.
|
|
218
|
+
Editor tab remounts therefore preserve Ctrl/Cmd+Z and Vim undo history without
|
|
219
|
+
leaving obsolete CodeMirror/Yjs observers behind. Managers are released when
|
|
220
|
+
the collaboration object is destroyed or loses edit permission.
|
|
221
|
+
|
|
222
|
+
TexLite probes the optional host `harper-cli` command at startup. It runs each
|
|
223
|
+
check in a private temporary TeX file after a narrow, linear server-side LaTeX
|
|
224
|
+
mask has removed comments, commands, references, math, tables, option syntax,
|
|
225
|
+
and code-like environments. This avoids browser/WASM startup work and keeps
|
|
226
|
+
malformed delimiters bounded. The service serializes checks, coalesces identical
|
|
227
|
+
source inputs for a short window, and returns Harper's safe replacement
|
|
228
|
+
suggestions. Spelling uses a red wavy underline and grammar uses yellow; a
|
|
229
|
+
context menu offers suggestions, while read-only members can inspect but cannot
|
|
230
|
+
apply them. The shared project dictionary is kept in SQLite and filters
|
|
231
|
+
project-specific spelling results in the browser. If the command is absent or
|
|
232
|
+
fails, CodeMirror enables the browser's built-in English spellchecker until it
|
|
233
|
+
retries.
|
|
234
|
+
|
|
235
|
+
Formatting is independent from the editor's local appearance. A user can
|
|
236
|
+
manually format a selection or enable the per-user/per-project “format before
|
|
237
|
+
compile” preference. The browser uses bundled `tex-fmt` WASM for `.tex`, `.cls`,
|
|
238
|
+
and `.sty`, and `bibtex-tidy` for `.bib`; the editor settings also provide a
|
|
239
|
+
per-user/per-project TOML options string passed to `tex-fmt`. The formatter and
|
|
240
|
+
text-diff calculation run in a lazily loaded Web Worker, so opening a project
|
|
241
|
+
does not wait for them and formatting does not block the editor UI. Formatter
|
|
242
|
+
diagnostic logs are shown as expandable warnings. A formatter failure reports
|
|
243
|
+
an error but does not prevent compilation. There is no silent Prettier fallback.
|
|
244
|
+
|
|
245
|
+
Before a formatter computes a replacement it acquires a short-lived, per-file
|
|
246
|
+
lease from the live Yjs room. The lease is held only for the format/snapshot,
|
|
247
|
+
final Yjs apply, and a durability flush; ordinary typing and compilation are
|
|
248
|
+
not blocked. A second formatter for the same path waits in a bounded FIFO queue,
|
|
249
|
+
and the server grants it only after the first session's update has been
|
|
250
|
+
received and flushed. Leases carry an expiring random token, renew before the
|
|
251
|
+
final apply, and are released automatically on timeout, permission loss, or
|
|
252
|
+
WebSocket disconnect. Because the lease is process-local, it is deliberately
|
|
253
|
+
scoped to TexLite's single-instance deployment; it is not a distributed lock.
|
|
254
|
+
If a source edit arrives while a formatter is working, the client discards the
|
|
255
|
+
stale replacement rather than applying offsets to newer text.
|
|
256
|
+
|
|
257
|
+
Project duplication flushes the live source room and copies the tree under a
|
|
258
|
+
short read barrier. Uploading a replacement text file also re-anchors existing
|
|
259
|
+
source comments against the old and new contents before notifying collaborators.
|
|
260
|
+
|
|
261
|
+
The outline follows `\input`, `\include`, and `\subfile` references and
|
|
262
|
+
jumps to source lines. The source and PDF panes expose explicit SyncTeX arrows,
|
|
263
|
+
and PDF double-click can request the corresponding source location. Search and
|
|
264
|
+
replace is project-wide, staged as one serialized operation, and records one
|
|
265
|
+
history version. Structured compile diagnostics resolve project-relative
|
|
266
|
+
file names and line numbers; the raw `latexmk` transcript remains available.
|
|
267
|
+
|
|
268
|
+
## Compilation and retained output
|
|
269
|
+
|
|
270
|
+
The project setting supplies the default root document. Project settings list
|
|
271
|
+
and accept only `.tex` files containing a real `\documentclass` declaration;
|
|
272
|
+
an imported project with exactly one `.tex` file may use that file as a
|
|
273
|
+
compatibility fallback. The server enforces the same rule for compile and
|
|
274
|
+
preview routes. A browser session may select another detected root, and only
|
|
275
|
+
the currently selected root is compiled. Compile state, logs, retained PDF,
|
|
276
|
+
artifacts, outline, and SyncTeX are keyed by root, so collaborators working on
|
|
277
|
+
different roots do not replace each other's result or compile notification.
|
|
278
|
+
|
|
279
|
+
Compilation follows this sequence:
|
|
280
|
+
|
|
281
|
+
1. Admission validates permissions/root selection and coalesces requests by
|
|
282
|
+
project, root, and a cheap source/settings generation.
|
|
283
|
+
2. After coalescing, TexLite flushes the room and captures a source snapshot.
|
|
284
|
+
A short snapshot barrier prevents autosave from modifying the source tree
|
|
285
|
+
while the asynchronous copy and digest run. Edits that arrive during the
|
|
286
|
+
barrier remain in Yjs memory until the snapshot is complete, so the
|
|
287
|
+
captured tree is internally consistent even when it is already older than
|
|
288
|
+
the live editor. Such a compile is accepted and the workspace labels the
|
|
289
|
+
retained PDF as based on an earlier snapshot; only a failed post-barrier
|
|
290
|
+
flush remains retryable.
|
|
291
|
+
3. Changed files are synchronized into an incremental compile workspace keyed
|
|
292
|
+
by project, root, engine, latexmkrc, and compiler arguments. The cache is
|
|
293
|
+
reused only for the same root; root-specific caches can compile concurrently
|
|
294
|
+
within the global `maxCompileJobs` limit.
|
|
295
|
+
4. `latexmk` runs with `-norc` and `-synctex=1`, line-oriented error output, and shell escape
|
|
296
|
+
disabled by default. Its process group, including `pdflatex`, BibTeX/Biber,
|
|
297
|
+
and other descendants, is terminated on timeout. latexmk itself performs
|
|
298
|
+
the repeated passes required by bibliography documents.
|
|
299
|
+
5. A successful PDF, SyncTeX file, log, and generated artifacts are copied into
|
|
300
|
+
an immutable run bundle. A small atomic manifest switch publishes it as the
|
|
301
|
+
latest result; an older bundle is retained briefly so an already-open PDF
|
|
302
|
+
request can finish.
|
|
303
|
+
|
|
304
|
+
The previous successful PDF remains visible during a new compile. The latest
|
|
305
|
+
published bundle is recovered after a restart and can be served before a cold
|
|
306
|
+
collaboration room is initialized. PDF requests support range responses for
|
|
307
|
+
PDF.js and expose the successful compile time and artifact size. The default
|
|
308
|
+
automatic loading policy uses a complete, cache-friendly response through 5 MB
|
|
309
|
+
and enables PDF.js byte-range loading above that threshold; deployments can
|
|
310
|
+
force either mode without changing project compiler settings. The output panel groups the PDF,
|
|
311
|
+
log, warnings, errors, generated artifacts, and recovery actions; clean-cache
|
|
312
|
+
and clean-artifact actions are for recovery rather than routine compilation.
|
|
313
|
+
Compile responses expose `Server-Timing` measurements for snapshot, cache
|
|
314
|
+
synchronization, LaTeX execution, artifact publication, and total request time
|
|
315
|
+
so queue and rendering regressions can be diagnosed. Artifact listing and
|
|
316
|
+
download endpoints treat removal of a run between manifest lookup and file
|
|
317
|
+
read as an empty list or 404, rather than exposing a filesystem race as a 500.
|
|
318
|
+
TexLite does not keep an unlimited browsable compile history: old unsuccessful
|
|
319
|
+
run rows and unreferenced bundles are pruned, while the latest successful
|
|
320
|
+
result for each root is retained.
|
|
321
|
+
|
|
322
|
+
## History and recovery
|
|
323
|
+
|
|
324
|
+
History records initial state, acknowledged collaborative saves, file/source
|
|
325
|
+
operations, compiler settings, Git operations, checkpoints, and restores.
|
|
326
|
+
Autosaves by the same author are coalesced within a two-minute window. File
|
|
327
|
+
contents are complete SHA-256-addressed objects; unchanged files are reused
|
|
328
|
+
across manifests. The retention defaults are 200 ordinary unlabeled versions
|
|
329
|
+
and 128 MB of deduplicated objects per project. Initial and labeled versions,
|
|
330
|
+
plus the current internal baseline, are protected and can make the soft limit
|
|
331
|
+
temporarily exceed its target. Retention pruning batches reference accounting
|
|
332
|
+
and removes unreferenced objects.
|
|
333
|
+
|
|
334
|
+
Owners can view storage statistics, delete one version, or clear all history
|
|
335
|
+
without changing current source files. Restore is an exclusive source
|
|
336
|
+
operation, reanchors comments against the before/after text, updates project
|
|
337
|
+
settings when restoring a complete version, and resets the collaboration epoch.
|
|
338
|
+
History is a recovery mechanism, not a substitute for backing up the complete
|
|
339
|
+
data directory.
|
|
340
|
+
|
|
341
|
+
## GitHub backup
|
|
342
|
+
|
|
343
|
+
The Git panel is project-owner-only. Git is optional at startup and is checked
|
|
344
|
+
when Git integration is used. A per-project GitHub token is encrypted in
|
|
345
|
+
SQLite; it is never placed in a remote URL or command-line argument. The local
|
|
346
|
+
repository lives in the project source directory, with temporary identity:
|
|
347
|
+
|
|
348
|
+
~~~text
|
|
349
|
+
user.name = project owner's username
|
|
350
|
+
user.email = <username>@texlite.com
|
|
351
|
+
~~~
|
|
352
|
+
|
|
353
|
+
For a fine-grained GitHub token in a trusted deployment, grant repository
|
|
354
|
+
Administration and Contents read/write permissions. “All repositories” is the
|
|
355
|
+
practical choice when a repository may be created after token configuration.
|
|
356
|
+
Only the owner can commit, push, checkout, restore, or configure the project
|
|
357
|
+
repository. Commit messages are entered explicitly. Normal checkout preserves
|
|
358
|
+
local changes and refuses conflicts; only the explicit force option discards
|
|
359
|
+
tracked, untracked, and ignored working-tree files. The Git operations use the
|
|
360
|
+
same project coordination and durable-flush boundary as other source
|
|
361
|
+
replacements.
|
|
362
|
+
|
|
363
|
+
## Data, backup, tags, and deletion
|
|
364
|
+
|
|
365
|
+
The default data layout is:
|
|
366
|
+
|
|
367
|
+
~~~text
|
|
368
|
+
<data-dir>/
|
|
369
|
+
├── .texlite.lock
|
|
370
|
+
│ └── owner.json
|
|
371
|
+
├── texlite.db
|
|
372
|
+
├── texlite.db-wal
|
|
373
|
+
├── texlite.db-shm
|
|
374
|
+
├── git-token.key
|
|
375
|
+
├── tmp/ and trash/
|
|
376
|
+
└── projects/
|
|
377
|
+
└── <project-id>/
|
|
378
|
+
├── source/
|
|
379
|
+
└── output/
|
|
380
|
+
└── .texlite/
|
|
381
|
+
├── cache/
|
|
382
|
+
├── runs/
|
|
383
|
+
└── history/
|
|
384
|
+
~~~
|
|
385
|
+
|
|
386
|
+
Back up `texlite.db`, `git-token.key`, and `projects/` together. Include the
|
|
387
|
+
SQLite WAL files in a live filesystem backup or use a SQLite-aware backup
|
|
388
|
+
procedure. The token encryption key is required to recover saved GitHub
|
|
389
|
+
credentials.
|
|
390
|
+
|
|
391
|
+
Tags and archive state are private to each user. A project can therefore have
|
|
392
|
+
different labels, filters, and archived/active visibility for different
|
|
393
|
+
collaborators. Deleting a project removes its database rows and source/output
|
|
394
|
+
directory; deletion uses a temporary trash rename when possible and a startup
|
|
395
|
+
reaper cleans abandoned trash/temp entries. Deleting a user removes sessions,
|
|
396
|
+
memberships, private tags, and comments remain attributable as “Deleted User”.
|
|
397
|
+
An administrator can transfer the user's owned projects to the current
|
|
398
|
+
administrator or delete them with their files. Project transfer keeps the old
|
|
399
|
+
owner as an editor and clears the project GitHub token so the new owner must
|
|
400
|
+
configure their own credential. Administrators do not otherwise receive
|
|
401
|
+
implicit access to another user's projects; they see a project only when they
|
|
402
|
+
own it or it has been explicitly shared with them. The last active
|
|
403
|
+
administrator cannot be removed or disabled.
|
|
404
|
+
|
|
405
|
+
## Known limitations and TODO
|
|
406
|
+
|
|
407
|
+
These items describe remaining engineering work rather than promises of a
|
|
408
|
+
particular release. Security items are especially important if the deployment
|
|
409
|
+
model expands beyond a small group of trusted users on localhost.
|
|
410
|
+
|
|
411
|
+
### Required before untrusted or public deployment
|
|
412
|
+
|
|
413
|
+
- [ ] Isolate compilation. LaTeX is not a security sandbox, and a project
|
|
414
|
+
`latexmkrc` is executable Perl. Prefer a dedicated low-privilege account or
|
|
415
|
+
container/sandbox and apply CPU, memory, process, and filesystem limits.
|
|
416
|
+
- [ ] Add deployment-aware HTTP protections: configurable trusted-proxy
|
|
417
|
+
handling, explicit Origin/CSRF validation for deployments that are not
|
|
418
|
+
localhost-only, and conservative response/security headers. Login limiting
|
|
419
|
+
and strict session-cookie defaults are present but are not a complete public
|
|
420
|
+
deployment policy.
|
|
421
|
+
- [x] Validate every PDF annotation URL against an explicit protocol allowlist,
|
|
422
|
+
including the URL that PDF.js labels as sanitized, before creating an
|
|
423
|
+
external browser link.
|
|
424
|
+
- [x] Harden compile-output serving. PDF and artifact listing/stat/copy paths
|
|
425
|
+
treat cleanup races as an empty result or 404 and never expose an expected
|
|
426
|
+
missing-file race as a 500.
|
|
427
|
+
|
|
428
|
+
### Correctness and recovery
|
|
429
|
+
|
|
430
|
+
- [ ] Make database/filesystem lifecycle operations fully crash-recoverable.
|
|
431
|
+
Project creation/import/duplication, project deletion, user cleanup, history
|
|
432
|
+
deletion, and temporary downloads still need explicit tombstones or startup
|
|
433
|
+
reconciliation for every failure point.
|
|
434
|
+
- [x] Refresh collaboration account/access data at message time and disconnect
|
|
435
|
+
revoked users; membership changes are also pushed to open clients.
|
|
436
|
+
- [x] Strengthen source-comment re-anchoring. A diff-mapped range is accepted
|
|
437
|
+
only when it still contains the original selected text; replacements and
|
|
438
|
+
ambiguous repeated text require matching surrounding context or are marked
|
|
439
|
+
orphaned for manual review.
|
|
440
|
+
- [x] Detect root documents as source files are opened and edited, ignoring
|
|
441
|
+
comments and common verbatim environments. The settings API and UI expose
|
|
442
|
+
only files with a real `\documentclass` declaration as candidates; a sole
|
|
443
|
+
`.tex` file remains a compatibility fallback for single-file imports. The
|
|
444
|
+
editor applies the same rule instead of trusting the configured path.
|
|
445
|
+
- [ ] Add failure-injection tests for crashes between source snapshot,
|
|
446
|
+
publication, database compile status updates, project deletion, and history
|
|
447
|
+
cleanup.
|
|
448
|
+
|
|
449
|
+
### Performance and maintainability
|
|
450
|
+
|
|
451
|
+
- [ ] Move large history snapshots, retention scans, and object garbage
|
|
452
|
+
collection away from synchronous request/startup paths, and add integrity
|
|
453
|
+
recovery for missing or orphaned history objects.
|
|
454
|
+
- [ ] Record per-project queue wait time and add a real-browser concurrency
|
|
455
|
+
benchmark covering a long compile alongside editing, PDF range requests,
|
|
456
|
+
SyncTeX, cleanup, and Git checkout.
|
|
457
|
+
- [ ] Continue modularization of the largest files, especially
|
|
458
|
+
`server/app.ts`, `server/collaboration.ts`, and `client/App.tsx`, so
|
|
459
|
+
authorization and coordination rules are easier to audit and test
|
|
460
|
+
independently.
|
package/NPM_TESTING.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Testing the npm package locally
|
|
2
|
+
|
|
3
|
+
Use the packed npm artifact for release testing. Installing the tarball into a
|
|
4
|
+
temporary npm prefix is closer to a real global installation than running the
|
|
5
|
+
source tree directly, and it does not modify the host's global npm packages.
|
|
6
|
+
|
|
7
|
+
## Build and inspect the package
|
|
8
|
+
|
|
9
|
+
Run the project checks first:
|
|
10
|
+
|
|
11
|
+
~~~bash
|
|
12
|
+
npm run typecheck
|
|
13
|
+
npm test
|
|
14
|
+
npm run build
|
|
15
|
+
npm pack --dry-run
|
|
16
|
+
~~~
|
|
17
|
+
|
|
18
|
+
The dry run lists the files that would be published. Check that it contains the
|
|
19
|
+
compiled server and `dist/client`, and does not contain local databases,
|
|
20
|
+
projects, credentials, or development-only files.
|
|
21
|
+
|
|
22
|
+
## Install the tarball into an isolated prefix
|
|
23
|
+
|
|
24
|
+
Create a temporary directory, pack the current version, and install it without
|
|
25
|
+
touching the real global npm installation:
|
|
26
|
+
|
|
27
|
+
~~~bash
|
|
28
|
+
TEST_ROOT="$(mktemp -d /tmp/texlite-package-test.XXXXXX)"
|
|
29
|
+
PACKAGE_VERSION="$(node -p "require('./package.json').version")"
|
|
30
|
+
PACKAGE_FILE="texlite-${PACKAGE_VERSION}.tgz"
|
|
31
|
+
|
|
32
|
+
npm pack --pack-destination "$TEST_ROOT"
|
|
33
|
+
npm_config_cache="$TEST_ROOT/npm-cache" \
|
|
34
|
+
npm install --prefix "$TEST_ROOT/prefix" --global "$TEST_ROOT/$PACKAGE_FILE"
|
|
35
|
+
|
|
36
|
+
"$TEST_ROOT/prefix/bin/texlite" --version
|
|
37
|
+
"$TEST_ROOT/prefix/bin/texlite" --help
|
|
38
|
+
~~~
|
|
39
|
+
|
|
40
|
+
The version in `PACKAGE_FILE` is read from `package.json`, so the commands also
|
|
41
|
+
work after a version bump.
|
|
42
|
+
|
|
43
|
+
## Test configuration, data paths, and environment checks
|
|
44
|
+
|
|
45
|
+
Use temporary XDG directories and a non-interactive administrator account:
|
|
46
|
+
|
|
47
|
+
~~~bash
|
|
48
|
+
XDG_CONFIG_HOME="$TEST_ROOT/config" \
|
|
49
|
+
XDG_DATA_HOME="$TEST_ROOT/data" \
|
|
50
|
+
TEXLITE_SITE_NAME='TexLite Package Test' \
|
|
51
|
+
TEXLITE_ADMIN_EMAIL='' \
|
|
52
|
+
TEXLITE_INIT_USERNAME=admin \
|
|
53
|
+
TEXLITE_INIT_DISPLAY_NAME=Administrator \
|
|
54
|
+
TEXLITE_INIT_PASSWORD='use-a-password-of-at-least-8-characters' \
|
|
55
|
+
"$TEST_ROOT/prefix/bin/texlite" init
|
|
56
|
+
|
|
57
|
+
XDG_CONFIG_HOME="$TEST_ROOT/config" \
|
|
58
|
+
XDG_DATA_HOME="$TEST_ROOT/data" \
|
|
59
|
+
"$TEST_ROOT/prefix/bin/texlite" config
|
|
60
|
+
|
|
61
|
+
XDG_CONFIG_HOME="$TEST_ROOT/config" \
|
|
62
|
+
XDG_DATA_HOME="$TEST_ROOT/data" \
|
|
63
|
+
"$TEST_ROOT/prefix/bin/texlite" doctor
|
|
64
|
+
~~~
|
|
65
|
+
|
|
66
|
+
The `config` output should show the temporary configuration and data paths.
|
|
67
|
+
`doctor` verifies the configuration, database, administrator, and host LaTeX
|
|
68
|
+
commands. Git is optional; add `--git` when Git integration should be checked.
|
|
69
|
+
|
|
70
|
+
To verify the configurable data directory explicitly:
|
|
71
|
+
|
|
72
|
+
~~~bash
|
|
73
|
+
XDG_CONFIG_HOME="$TEST_ROOT/config" \
|
|
74
|
+
XDG_DATA_HOME="$TEST_ROOT/data" \
|
|
75
|
+
TEXLITE_DATA_DIR="$TEST_ROOT/custom-data" \
|
|
76
|
+
"$TEST_ROOT/prefix/bin/texlite" config
|
|
77
|
+
~~~
|
|
78
|
+
|
|
79
|
+
## Test the foreground server
|
|
80
|
+
|
|
81
|
+
Run the installed package in the foreground:
|
|
82
|
+
|
|
83
|
+
~~~bash
|
|
84
|
+
XDG_CONFIG_HOME="$TEST_ROOT/config" \
|
|
85
|
+
XDG_DATA_HOME="$TEST_ROOT/data" \
|
|
86
|
+
"$TEST_ROOT/prefix/bin/texlite" serve
|
|
87
|
+
~~~
|
|
88
|
+
|
|
89
|
+
Open <http://127.0.0.1:3000> in a browser and exercise login, project
|
|
90
|
+
creation, editing, compilation, file upload, comments, and PDF preview. Press
|
|
91
|
+
Ctrl+C to stop the server.
|
|
92
|
+
|
|
93
|
+
The package requires Node.js 24 or newer and the LaTeX engines enabled in the
|
|
94
|
+
selected configuration.
|
|
95
|
+
|
|
96
|
+
## Test the PM2 lifecycle in isolation
|
|
97
|
+
|
|
98
|
+
Use both a private `PM2_HOME` and a test-only port. Every lifecycle command must
|
|
99
|
+
receive the same environment so it addresses the same configuration and PM2
|
|
100
|
+
daemon without touching the host user's real processes:
|
|
101
|
+
|
|
102
|
+
~~~bash
|
|
103
|
+
PM2_HOME="$TEST_ROOT/pm2" \
|
|
104
|
+
XDG_CONFIG_HOME="$TEST_ROOT/config" \
|
|
105
|
+
XDG_DATA_HOME="$TEST_ROOT/data" \
|
|
106
|
+
TEXLITE_PORT=3300 \
|
|
107
|
+
"$TEST_ROOT/prefix/bin/texlite" start
|
|
108
|
+
|
|
109
|
+
PM2_HOME="$TEST_ROOT/pm2" \
|
|
110
|
+
XDG_CONFIG_HOME="$TEST_ROOT/config" \
|
|
111
|
+
XDG_DATA_HOME="$TEST_ROOT/data" \
|
|
112
|
+
TEXLITE_PORT=3300 \
|
|
113
|
+
"$TEST_ROOT/prefix/bin/texlite" status
|
|
114
|
+
|
|
115
|
+
PM2_HOME="$TEST_ROOT/pm2" \
|
|
116
|
+
XDG_CONFIG_HOME="$TEST_ROOT/config" \
|
|
117
|
+
XDG_DATA_HOME="$TEST_ROOT/data" \
|
|
118
|
+
TEXLITE_PORT=3300 \
|
|
119
|
+
"$TEST_ROOT/prefix/bin/texlite" stop
|
|
120
|
+
~~~
|
|
121
|
+
|
|
122
|
+
The custom XDG configuration receives a path-derived PM2 name such as
|
|
123
|
+
`texlite-a1b2c3d4`; the private `PM2_HOME` additionally prevents the test daemon
|
|
124
|
+
and logs from mixing with the normal installation.
|
|
125
|
+
|
|
126
|
+
Remove the temporary test installation when finished:
|
|
127
|
+
|
|
128
|
+
~~~bash
|
|
129
|
+
rm -rf "$TEST_ROOT"
|
|
130
|
+
~~~
|