apple-books-export 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Denis Moskalets (denya.msk@gmail.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # Apple Books Export
2
+
3
+ Export all your highlights, bookmarks, and notes from Apple Books on macOS.
4
+
5
+ ## What is this?
6
+
7
+ If you highlight books in Apple Books, this tool lets you export all your highlights to HTML, Markdown, JSON, or CSV. Perfect for:
8
+
9
+ - Backing up your highlights
10
+ - Searching through all your notes
11
+ - Sharing quotes from books you've read
12
+ - Importing highlights into note-taking apps
13
+
14
+ ## Quick Start
15
+
16
+ No installation needed! Just run:
17
+
18
+ ```bash
19
+ bunx apple-books-export
20
+ ```
21
+
22
+ This creates an `apple-books-export.html` file with all your highlights in a searchable, interactive page.
23
+
24
+ **Don't have Bun?** Install it with: `curl -fsSL https://bun.sh/install | bash`
25
+
26
+ Or use Node.js instead: `npx apple-books-export`
27
+
28
+ ## Common Uses
29
+
30
+ ```bash
31
+ # Export to searchable HTML (default)
32
+ bunx apple-books-export
33
+
34
+ # Export to Markdown files (one per book)
35
+ bunx apple-books-export markdown
36
+
37
+ # Export to a single JSON file
38
+ bunx apple-books-export json --single-file
39
+
40
+ # Export only highlights (skip bookmarks)
41
+ bunx apple-books-export --highlights-only
42
+
43
+ # Export only yellow highlights
44
+ bunx apple-books-export --colors yellow
45
+ ```
46
+
47
+ ## Export Formats
48
+
49
+ ### HTML (default)
50
+ Creates a single webpage with all your highlights. Includes search, color-coding, and works offline.
51
+
52
+ ### Markdown
53
+ Creates readable text files with YAML metadata. Great for importing into Obsidian or Notion.
54
+
55
+ ### JSON
56
+ Exports raw data with all metadata. Useful for custom processing or analysis.
57
+
58
+ ### CSV
59
+ Spreadsheet format for Excel or data analysis.
60
+
61
+ ## Options
62
+
63
+ Run `bunx apple-books-export --help` to see all options.
64
+
65
+ Common options:
66
+ - `--output <path>` - Custom output location
67
+ - `--single-file` - Combine all books into one file (for Markdown/JSON)
68
+ - `--highlights-only` - Skip bookmarks and notes
69
+ - `--colors yellow,green` - Only export specific highlight colors
70
+
71
+ ## Troubleshooting
72
+
73
+ **"Database not found"** - Make sure you have Apple Books installed and have created at least one highlight or bookmark.
74
+
75
+ **No highlights exported** - Check that you're not using filters that exclude all your highlights (e.g., `--colors pink` when you only have yellow highlights).
76
+
77
+ ## License
78
+
79
+ MIT - Free to use and modify.
80
+
81
+ ## Credits
82
+
83
+ Built with [Bun](https://bun.sh). Inspired by [jladicos/apple-books-highlights](https://github.com/jladicos/apple-books-highlights) and [angela-zhao/apple-books-annotations-exporter](https://github.com/angela-zhao/apple-books-annotations-exporter).
@@ -0,0 +1,37 @@
1
+ import type { Annotation, Book, RawAnnotation } from "./types.js";
2
+ /**
3
+ * Convert Apple's 2001-01-01 epoch timestamp to JavaScript Date
4
+ */
5
+ export declare function convertAppleDate(timestamp: number): Date;
6
+ /**
7
+ * Find Apple Books SQLite database files
8
+ */
9
+ export declare function findDatabases(customPaths?: {
10
+ annotations?: string;
11
+ library?: string;
12
+ }): {
13
+ annotationsDb: string;
14
+ libraryDb: string;
15
+ };
16
+ /**
17
+ * Query all annotations with book metadata
18
+ */
19
+ export declare function queryAnnotations(annotationsDbPath: string, libraryDbPath: string): Promise<RawAnnotation[]>;
20
+ /**
21
+ * Transform raw annotations into structured Annotation objects
22
+ */
23
+ export declare function transformAnnotations(rawAnnotations: RawAnnotation[]): Annotation[];
24
+ /**
25
+ * Group annotations by book
26
+ */
27
+ export declare function groupByBook(rawAnnotations: RawAnnotation[]): Book[];
28
+ /**
29
+ * Filter annotations based on export options
30
+ */
31
+ export declare function filterAnnotations(annotations: Annotation[], options: {
32
+ includeHighlights: boolean;
33
+ includeBookmarks: boolean;
34
+ includeNotes: boolean;
35
+ colorFilters?: string[];
36
+ }): Annotation[];
37
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAmC,MAAM,YAAY,CAAC;AAOnG;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAMxD;AA4CD;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IACvF,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAyCA;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,EAAE,CAAC,CA+B1B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,aAAa,EAAE,GAAG,UAAU,EAAE,CAiBlF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,cAAc,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,CAkCnE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,UAAU,EAAE,EACzB,OAAO,EAAE;IACP,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,GACA,UAAU,EAAE,CAcd"}
@@ -0,0 +1,187 @@
1
+ import { Database } from "./db-adapter.js";
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ import { existsSync, readdirSync } from "fs";
5
+ const APPLE_BOOKS_CONTAINER = join(homedir(), "Library/Containers/com.apple.iBooksX/Data/Documents");
6
+ /**
7
+ * Convert Apple's 2001-01-01 epoch timestamp to JavaScript Date
8
+ */
9
+ export function convertAppleDate(timestamp) {
10
+ // Apple epoch starts at 2001-01-01 00:00:00 UTC
11
+ // Unix epoch starts at 1970-01-01 00:00:00 UTC
12
+ // Difference is 978307200 seconds
13
+ const APPLE_EPOCH_OFFSET = 978307200;
14
+ return new Date((timestamp + APPLE_EPOCH_OFFSET) * 1000);
15
+ }
16
+ /**
17
+ * Map Apple's annotation style code to type and color
18
+ */
19
+ function mapAnnotationStyle(style) {
20
+ // Based on research:
21
+ // 0 = underline (highlight)
22
+ // 1 = green highlight
23
+ // 2 = blue highlight
24
+ // 3 = yellow highlight
25
+ // 4 = pink highlight
26
+ // 5 = purple highlight
27
+ const styleMap = {
28
+ 0: { type: 'highlight', color: 'underline' },
29
+ 1: { type: 'highlight', color: 'green' },
30
+ 2: { type: 'highlight', color: 'blue' },
31
+ 3: { type: 'highlight', color: 'yellow' },
32
+ 4: { type: 'highlight', color: 'pink' },
33
+ 5: { type: 'highlight', color: 'purple' },
34
+ };
35
+ return styleMap[style] || { type: 'highlight', color: 'yellow' };
36
+ }
37
+ /**
38
+ * Determine annotation type based on content
39
+ */
40
+ function determineAnnotationType(raw) {
41
+ // If it has a note, it's a note annotation
42
+ if (raw.note && raw.note.trim().length > 0) {
43
+ return 'note';
44
+ }
45
+ // If it has no selected text, it's a bookmark
46
+ if (!raw.text || raw.text.trim().length === 0) {
47
+ return 'bookmark';
48
+ }
49
+ // Otherwise it's a highlight
50
+ return 'highlight';
51
+ }
52
+ /**
53
+ * Find Apple Books SQLite database files
54
+ */
55
+ export function findDatabases(customPaths) {
56
+ if (customPaths?.annotations && customPaths?.library) {
57
+ return {
58
+ annotationsDb: customPaths.annotations,
59
+ libraryDb: customPaths.library,
60
+ };
61
+ }
62
+ // Find AEAnnotation database
63
+ const annotationsDir = join(APPLE_BOOKS_CONTAINER, "AEAnnotation");
64
+ if (!existsSync(annotationsDir)) {
65
+ throw new Error(`Annotations directory not found: ${annotationsDir}`);
66
+ }
67
+ const annotationFiles = readdirSync(annotationsDir).filter(f => f.startsWith("AEAnnotation_") && f.endsWith(".sqlite"));
68
+ if (annotationFiles.length === 0) {
69
+ throw new Error(`No annotation database found in ${annotationsDir}`);
70
+ }
71
+ const annotationsDb = join(annotationsDir, annotationFiles[0]);
72
+ // Find BKLibrary database
73
+ const libraryDir = join(APPLE_BOOKS_CONTAINER, "BKLibrary");
74
+ if (!existsSync(libraryDir)) {
75
+ throw new Error(`Library directory not found: ${libraryDir}`);
76
+ }
77
+ const libraryFiles = readdirSync(libraryDir).filter(f => f.startsWith("BKLibrary") && f.endsWith(".sqlite"));
78
+ if (libraryFiles.length === 0) {
79
+ throw new Error(`No library database found in ${libraryDir}`);
80
+ }
81
+ const libraryDb = join(libraryDir, libraryFiles[0]);
82
+ return { annotationsDb, libraryDb };
83
+ }
84
+ /**
85
+ * Query all annotations with book metadata
86
+ */
87
+ export async function queryAnnotations(annotationsDbPath, libraryDbPath) {
88
+ const annotationsDb = await Database.create(annotationsDbPath, { readonly: true });
89
+ // Attach the library database
90
+ annotationsDb.run(`ATTACH DATABASE '${libraryDbPath}' AS library`);
91
+ const query = `
92
+ SELECT
93
+ ZAEANNOTATION.Z_PK as id,
94
+ ZAEANNOTATION.ZANNOTATIONASSETID as assetId,
95
+ ZAEANNOTATION.ZANNOTATIONSELECTEDTEXT as text,
96
+ ZAEANNOTATION.ZANNOTATIONNOTE as note,
97
+ ZAEANNOTATION.ZANNOTATIONSTYLE as style,
98
+ ZAEANNOTATION.ZFUTUREPROOFING5 as location,
99
+ ZAEANNOTATION.ZANNOTATIONCREATIONDATE as createdAt,
100
+ ZAEANNOTATION.ZANNOTATIONMODIFICATIONDATE as modifiedAt,
101
+ ZAEANNOTATION.ZANNOTATIONDELETED as deleted,
102
+ library.ZBKLIBRARYASSET.ZTITLE as title,
103
+ library.ZBKLIBRARYASSET.ZAUTHOR as author,
104
+ library.ZBKLIBRARYASSET.ZGENRE as genre
105
+ FROM ZAEANNOTATION
106
+ LEFT JOIN library.ZBKLIBRARYASSET
107
+ ON ZAEANNOTATION.ZANNOTATIONASSETID = library.ZBKLIBRARYASSET.ZASSETID
108
+ WHERE ZAEANNOTATION.ZANNOTATIONDELETED = 0
109
+ ORDER BY title, createdAt
110
+ `;
111
+ const results = annotationsDb.query(query).all();
112
+ annotationsDb.close();
113
+ return results;
114
+ }
115
+ /**
116
+ * Transform raw annotations into structured Annotation objects
117
+ */
118
+ export function transformAnnotations(rawAnnotations) {
119
+ return rawAnnotations.map(raw => {
120
+ const type = determineAnnotationType(raw);
121
+ const { color } = mapAnnotationStyle(raw.style);
122
+ return {
123
+ id: raw.id,
124
+ type,
125
+ color,
126
+ text: raw.text,
127
+ note: raw.note,
128
+ location: raw.location,
129
+ chapter: null, // Apple Books doesn't store chapter info in accessible way
130
+ createdAt: convertAppleDate(raw.createdAt),
131
+ modifiedAt: convertAppleDate(raw.modifiedAt),
132
+ };
133
+ });
134
+ }
135
+ /**
136
+ * Group annotations by book
137
+ */
138
+ export function groupByBook(rawAnnotations) {
139
+ const bookMap = new Map();
140
+ for (const raw of rawAnnotations) {
141
+ const assetId = raw.assetId;
142
+ if (!bookMap.has(assetId)) {
143
+ bookMap.set(assetId, {
144
+ assetId,
145
+ title: raw.title,
146
+ author: raw.author,
147
+ genre: raw.genre,
148
+ annotations: [],
149
+ });
150
+ }
151
+ const book = bookMap.get(assetId);
152
+ const type = determineAnnotationType(raw);
153
+ const { color } = mapAnnotationStyle(raw.style);
154
+ book.annotations.push({
155
+ id: raw.id,
156
+ type,
157
+ color,
158
+ text: raw.text,
159
+ note: raw.note,
160
+ location: raw.location,
161
+ chapter: null,
162
+ createdAt: convertAppleDate(raw.createdAt),
163
+ modifiedAt: convertAppleDate(raw.modifiedAt),
164
+ });
165
+ }
166
+ return Array.from(bookMap.values());
167
+ }
168
+ /**
169
+ * Filter annotations based on export options
170
+ */
171
+ export function filterAnnotations(annotations, options) {
172
+ return annotations.filter(ann => {
173
+ // Filter by type
174
+ if (ann.type === 'highlight' && !options.includeHighlights)
175
+ return false;
176
+ if (ann.type === 'bookmark' && !options.includeBookmarks)
177
+ return false;
178
+ if (ann.type === 'note' && !options.includeNotes)
179
+ return false;
180
+ // Filter by color
181
+ if (options.colorFilters && options.colorFilters.length > 0) {
182
+ if (!options.colorFilters.includes(ann.color))
183
+ return false;
184
+ }
185
+ return true;
186
+ });
187
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Database adapter for dual runtime support (Bun + Node.js)
3
+ *
4
+ * Provides a unified SQLite interface that works with:
5
+ * - Bun's built-in bun:sqlite (zero dependencies)
6
+ * - Node.js's better-sqlite3 (auto-installed)
7
+ */
8
+ type QueryResult = any[];
9
+ interface DatabaseInterface {
10
+ query(sql: string): {
11
+ all(): QueryResult;
12
+ };
13
+ run(sql: string): void;
14
+ close(): void;
15
+ }
16
+ /**
17
+ * Unified Database class that works in both Bun and Node.js
18
+ *
19
+ * This uses a factory pattern since constructors can't be async.
20
+ */
21
+ export declare class Database implements DatabaseInterface {
22
+ private db;
23
+ private isBun;
24
+ private constructor();
25
+ /**
26
+ * Create a new Database instance (async factory)
27
+ */
28
+ static create(path: string, options?: {
29
+ readonly?: boolean;
30
+ }): Promise<Database>;
31
+ query(sql: string): any;
32
+ run(sql: string): void;
33
+ close(): void;
34
+ }
35
+ export {};
36
+ //# sourceMappingURL=db-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-adapter.d.ts","sourceRoot":"","sources":["../src/db-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,KAAK,WAAW,GAAG,GAAG,EAAE,CAAC;AAEzB,UAAU,iBAAiB;IACzB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,GAAG,IAAI,WAAW,CAAA;KAAE,CAAC;IAC3C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,IAAI,IAAI,CAAC;CACf;AASD;;;;GAIG;AACH,qBAAa,QAAS,YAAW,iBAAiB;IAChD,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,KAAK,CAAU;IAEvB,OAAO;IAKP;;OAEG;WACU,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAmBtF,KAAK,CAAC,GAAG,EAAE,MAAM;IAYjB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAUtB,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Database adapter for dual runtime support (Bun + Node.js)
3
+ *
4
+ * Provides a unified SQLite interface that works with:
5
+ * - Bun's built-in bun:sqlite (zero dependencies)
6
+ * - Node.js's better-sqlite3 (auto-installed)
7
+ */
8
+ /**
9
+ * Detect runtime environment
10
+ */
11
+ function isBunRuntime() {
12
+ return typeof globalThis.Bun !== "undefined";
13
+ }
14
+ /**
15
+ * Unified Database class that works in both Bun and Node.js
16
+ *
17
+ * This uses a factory pattern since constructors can't be async.
18
+ */
19
+ export class Database {
20
+ db;
21
+ isBun;
22
+ constructor(db, isBun) {
23
+ this.db = db;
24
+ this.isBun = isBun;
25
+ }
26
+ /**
27
+ * Create a new Database instance (async factory)
28
+ */
29
+ static async create(path, options) {
30
+ const isBun = isBunRuntime();
31
+ if (isBun) {
32
+ // Bun runtime - use built-in bun:sqlite
33
+ // Dynamic import works in both runtimes
34
+ const { Database: BunDB } = await import('bun:sqlite');
35
+ const db = new BunDB(path, options);
36
+ return new Database(db, isBun);
37
+ }
38
+ else {
39
+ // Node.js runtime - use better-sqlite3
40
+ // Dynamic import works in ESM
41
+ const BetterSqlite3Module = await import('better-sqlite3');
42
+ const BetterSqlite3 = BetterSqlite3Module.default || BetterSqlite3Module;
43
+ const db = new BetterSqlite3(path, options);
44
+ return new Database(db, isBun);
45
+ }
46
+ }
47
+ query(sql) {
48
+ if (this.isBun) {
49
+ // Bun: db.query(sql).all()
50
+ return this.db.query(sql);
51
+ }
52
+ else {
53
+ // Node.js: db.prepare(sql).all()
54
+ return {
55
+ all: () => this.db.prepare(sql).all()
56
+ };
57
+ }
58
+ }
59
+ run(sql) {
60
+ if (this.isBun) {
61
+ // Bun: db.run(sql)
62
+ this.db.run(sql);
63
+ }
64
+ else {
65
+ // Node.js: db.prepare(sql).run()
66
+ this.db.prepare(sql).run();
67
+ }
68
+ }
69
+ close() {
70
+ this.db.close();
71
+ }
72
+ }
@@ -0,0 +1,6 @@
1
+ import type { Book } from "../types.js";
2
+ /**
3
+ * Export books to CSV file
4
+ */
5
+ export declare function exportToCsv(books: Book[], outputPath: string): string;
6
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/exporters/csv.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AA4BxC;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAgDrE"}
@@ -0,0 +1,70 @@
1
+ import { writeFileSync, mkdirSync } from "fs";
2
+ import { dirname } from "path";
3
+ /**
4
+ * Escape CSV field value
5
+ */
6
+ function escapeCsvField(value) {
7
+ if (value === null || value === undefined) {
8
+ return '';
9
+ }
10
+ const stringValue = String(value);
11
+ // If the value contains quotes, commas, or newlines, it needs to be quoted
12
+ if (stringValue.includes('"') || stringValue.includes(',') || stringValue.includes('\n')) {
13
+ // Escape quotes by doubling them
14
+ return `"${stringValue.replace(/"/g, '""')}"`;
15
+ }
16
+ return stringValue;
17
+ }
18
+ /**
19
+ * Format date for CSV
20
+ */
21
+ function formatDate(date) {
22
+ return date.toISOString().replace('T', ' ').substring(0, 19);
23
+ }
24
+ /**
25
+ * Export books to CSV file
26
+ */
27
+ export function exportToCsv(books, outputPath) {
28
+ // Ensure parent directory exists
29
+ mkdirSync(dirname(outputPath), { recursive: true });
30
+ // CSV header
31
+ const headers = [
32
+ 'assetId',
33
+ 'title',
34
+ 'author',
35
+ 'genre',
36
+ 'annotationId',
37
+ 'type',
38
+ 'color',
39
+ 'text',
40
+ 'note',
41
+ 'location',
42
+ 'chapter',
43
+ 'createdAt',
44
+ 'modifiedAt',
45
+ ];
46
+ let csv = headers.join(',') + '\n';
47
+ // Add rows
48
+ for (const book of books) {
49
+ for (const annotation of book.annotations) {
50
+ const row = [
51
+ escapeCsvField(book.assetId),
52
+ escapeCsvField(book.title),
53
+ escapeCsvField(book.author),
54
+ escapeCsvField(book.genre),
55
+ escapeCsvField(String(annotation.id)),
56
+ escapeCsvField(annotation.type),
57
+ escapeCsvField(annotation.color),
58
+ escapeCsvField(annotation.text),
59
+ escapeCsvField(annotation.note),
60
+ escapeCsvField(annotation.location),
61
+ escapeCsvField(annotation.chapter),
62
+ escapeCsvField(formatDate(annotation.createdAt)),
63
+ escapeCsvField(formatDate(annotation.modifiedAt)),
64
+ ];
65
+ csv += row.join(',') + '\n';
66
+ }
67
+ }
68
+ writeFileSync(outputPath, csv, 'utf-8');
69
+ return outputPath;
70
+ }
@@ -0,0 +1,3 @@
1
+ import type { Book } from '../types.js';
2
+ export declare function exportToHtml(books: Book[], outputPath: string): string;
3
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/exporters/html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,aAAa,CAAC;AAqHpD,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CA+qBtE"}