shamela 1.2.3 → 1.3.1

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.MD CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2024 Ragaeeb Haq>
1
+ Copyright 2024-2025 Ragaeeb Haq>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![wakatime](https://wakatime.com/badge/user/a0b906ce-b8e7-4463-8bce-383238df6d4b/project/faef70ab-efdb-448b-ab83-0fc66c95888e.svg)](https://wakatime.com/badge/user/a0b906ce-b8e7-4463-8bce-383238df6d4b/project/faef70ab-efdb-448b-ab83-0fc66c95888e)
4
4
  [![E2E](https://github.com/ragaeeb/shamela/actions/workflows/e2e.yml/badge.svg)](https://github.com/ragaeeb/shamela/actions/workflows/e2e.yml)
5
+ [![Vercel Deploy](https://deploy-badge.vercel.app/vercel/shamela)](https://shamela.vercel.app)
5
6
  [![Node.js CI](https://github.com/ragaeeb/shamela/actions/workflows/build.yml/badge.svg)](https://github.com/ragaeeb/shamela/actions/workflows/build.yml) ![GitHub License](https://img.shields.io/github/license/ragaeeb/shamela)
6
7
  ![GitHub Release](https://img.shields.io/github/v/release/ragaeeb/shamela)
7
8
  [![codecov](https://codecov.io/gh/ragaeeb/shamela/graph/badge.svg?token=PK55V1R324)](https://codecov.io/gh/ragaeeb/shamela)
@@ -12,7 +13,7 @@
12
13
  ![GitHub issues](https://img.shields.io/github/issues/ragaeeb/shamela)
13
14
  ![GitHub stars](https://img.shields.io/github/stars/ragaeeb/shamela?style=social)
14
15
 
15
- A `Node.js` library for accessing and downloading Maktabah Shamela v4 APIs. This library provides easy-to-use functions to interact with the Shamela API, download master and book databases, and retrieve book data programmatically.
16
+ A universal TypeScript library for accessing and downloading Maktabah Shamela v4 APIs. The package runs in both Node.js and modern browsers, providing ergonomic helpers to interact with the Shamela API, download master and book databases, and retrieve book data programmatically.
16
17
 
17
18
  ## Table of Contents
18
19
 
@@ -20,19 +21,23 @@ A `Node.js` library for accessing and downloading Maktabah Shamela v4 APIs. This
20
21
  - [Environment Variables](#environment-variables)
21
22
  - [Usage](#usage)
22
23
  - [Getting Started](#getting-started)
24
+ - [Next.js / Bundled Environments](#nextjs--bundled-environments)
23
25
  - [API Functions](#api-functions)
24
26
  - [getMasterMetadata](#getmastermetadata)
25
27
  - [downloadMasterDatabase](#downloadmasterdatabase)
26
28
  - [getBookMetadata](#getbookmetadata)
27
29
  - [downloadBook](#downloadbook)
28
30
  - [getBook](#getbook)
31
+ - [getMaster](#getmaster)
29
32
  - [getCoverUrl](#getcoverurl)
30
33
  - [Examples](#examples)
31
34
  - [Downloading the Master Database](#downloading-the-master-database)
32
35
  - [Downloading a Book](#downloading-a-book)
33
36
  - [Retrieving Book Data](#retrieving-book-data)
34
- - [Getting Book Cover URLs](#getting-book-cover-urls)
37
+ - [Retrieving Master Data in memory](#retrieving-master-data-in-memory)
35
38
  - [Data Structures](#data-structures)
39
+ - [Next.js demo](#nextjs-demo)
40
+ - [Troubleshooting](#troubleshooting)
36
41
  - [Testing](#testing)
37
42
  - [License](#license)
38
43
 
@@ -67,6 +72,7 @@ Before using the library, you need to set up some environment variables for API
67
72
  - `SHAMELA_API_KEY`: Your API key for accessing the Shamela API.
68
73
  - `SHAMELA_API_MASTER_PATCH_ENDPOINT`: The endpoint URL for the master database patches.
69
74
  - `SHAMELA_API_BOOKS_ENDPOINT`: The base endpoint URL for book-related API calls.
75
+ - `SHAMELA_SQLJS_WASM_URL` (optional): Override the default CDN URL used to load the `sql.js` WebAssembly binary when running in the browser.
70
76
 
71
77
  You can set these variables in a `.env` file at the root of your project:
72
78
 
@@ -74,8 +80,34 @@ You can set these variables in a `.env` file at the root of your project:
74
80
  SHAMELA_API_KEY=your_api_key_here
75
81
  SHAMELA_API_MASTER_PATCH_ENDPOINT=https://shamela.ws/api/master_patch
76
82
  SHAMELA_API_BOOKS_ENDPOINT=https://shamela.ws/api/books
83
+ # Optional when you host sql-wasm.wasm yourself
84
+ # SHAMELA_SQLJS_WASM_URL=https://example.com/sql-wasm.wasm
77
85
  ```
78
86
 
87
+ ### Runtime configuration (browsers and serverless)
88
+
89
+ When you cannot rely on environment variables—such as when running inside a browser, an edge worker, or a serverless function—use the `configure` helper to provide credentials at runtime:
90
+
91
+ ```ts
92
+ import { configure } from 'shamela';
93
+
94
+ configure({
95
+ apiKey: process.env.NEXT_PUBLIC_SHAMELA_KEY,
96
+ booksEndpoint: 'https://shamela.ws/api/books',
97
+ masterPatchEndpoint: 'https://shamela.ws/api/master_patch',
98
+ // Optional: host sql-wasm.wasm yourself to control caching/CDN placement
99
+ sqlJsWasmUrl: '/assets/sql-wasm.wasm',
100
+ // Optional: integrate with your application's logging system
101
+ logger: console,
102
+ // Optional: provide a custom fetch implementation (for tests or SSR)
103
+ fetchImplementation: fetch,
104
+ });
105
+ ```
106
+
107
+ You can call `configure` multiple times—values are merged, so later calls update only the keys you pass in.
108
+
109
+ The optional `logger` must expose `debug`, `info`, `warn`, and `error` methods. When omitted, the library stays silent by default.
110
+
79
111
  ## Usage
80
112
 
81
113
  ### Getting Started
@@ -89,10 +121,92 @@ import {
89
121
  getBookMetadata,
90
122
  downloadBook,
91
123
  getBook,
124
+ getMaster,
92
125
  getCoverUrl,
93
126
  } from 'shamela';
94
127
  ```
95
128
 
129
+ ### Next.js / Bundled Environments
130
+
131
+ When using this library in Next.js or other bundled environments (webpack/Turbopack), you need some additional configuration to ensure the sql.js WASM file is loaded correctly.
132
+
133
+ #### 1. Update your Next.js configuration
134
+
135
+ Add the following to your `next.config.js` or `next.config.ts`:
136
+
137
+ ```typescript
138
+ import type { NextConfig } from 'next';
139
+
140
+ const nextConfig: NextConfig = {
141
+ experimental: {
142
+ serverComponentsExternalPackages: ['shamela', 'sql.js'],
143
+ },
144
+ serverExternalPackages: ['shamela', 'sql.js'],
145
+ // ... rest of your config
146
+ };
147
+
148
+ export default nextConfig;
149
+ ```
150
+
151
+ This tells Next.js to exclude these packages from bundling and load them directly from `node_modules`.
152
+
153
+ #### 2. Create a server-only configuration file
154
+
155
+ Create a configuration file that will be imported only in server-side code:
156
+
157
+ **Option A: Using the `createNodeConfig` helper (Recommended)**
158
+
159
+ ```typescript
160
+ // lib/shamela-server.ts
161
+ import { configure, createNodeConfig } from 'shamela';
162
+
163
+ // Configure once when this module loads
164
+ configure(createNodeConfig({
165
+ apiKey: process.env.SHAMELA_API_KEY,
166
+ booksEndpoint: process.env.SHAMELA_BOOKS_ENDPOINT,
167
+ masterPatchEndpoint: process.env.SHAMELA_MASTER_ENDPOINT,
168
+ }));
169
+
170
+ // Re-export the functions you need
171
+ export { getBookMetadata, downloadBook, getMaster, getBook } from 'shamela';
172
+ ```
173
+
174
+ **Option B: Manual configuration**
175
+
176
+ ```typescript
177
+ // lib/shamela-server.ts
178
+ import { configure } from 'shamela';
179
+ import { join } from 'node:path';
180
+
181
+ configure({
182
+ sqlJsWasmUrl: join(process.cwd(), 'node_modules', 'sql.js', 'dist', 'sql-wasm.wasm'),
183
+ apiKey: process.env.SHAMELA_API_KEY,
184
+ booksEndpoint: process.env.SHAMELA_BOOKS_ENDPOINT,
185
+ masterPatchEndpoint: process.env.SHAMELA_MASTER_ENDPOINT,
186
+ });
187
+
188
+ export { getBookMetadata, downloadBook, getMaster, getBook } from 'shamela';
189
+ ```
190
+
191
+ #### 3. Use in Server Actions or API Routes
192
+
193
+ ```typescript
194
+ 'use server';
195
+
196
+ import { getBookMetadata, downloadBook } from '@/lib/shamela-server';
197
+
198
+ export async function downloadBookAction(bookId: number) {
199
+ const metadata = await getBookMetadata(bookId);
200
+ const result = await downloadBook(bookId, {
201
+ bookMetadata: metadata,
202
+ outputFile: { path: `./books/${bookId}.db` }
203
+ });
204
+ return result;
205
+ }
206
+ ```
207
+
208
+ **Important:** Never import `shamela` directly in your `layout.tsx` or client components. Only use it in server-side code (Server Actions, API Routes, or Server Components).
209
+
96
210
  ### API Functions
97
211
 
98
212
  #### getMasterMetadata
@@ -239,6 +353,26 @@ console.log(bookData.titles?.length); // Number of title entries
239
353
  console.log(bookData.pages[0].content); // Content of the first page
240
354
  ```
241
355
 
356
+ #### getMaster
357
+
358
+ Retrieves the entire master dataset (authors, books, categories) as a JavaScript object, including the version number that the
359
+ API reports for the snapshot.
360
+
361
+ ```typescript
362
+ getMaster(): Promise<MasterData>
363
+ ```
364
+
365
+ **Returns:** Promise that resolves to the complete master dataset with version metadata
366
+
367
+ **Example:**
368
+
369
+ ```javascript
370
+ const masterData = await getMaster();
371
+ console.log(masterData.version); // Version of the downloaded master database
372
+ console.log(masterData.books.length); // Number of books available
373
+ console.log(masterData.categories.length); // Number of categories available
374
+ ```
375
+
242
376
  #### getCoverUrl
243
377
 
244
378
  Generates the URL for a book's cover image.
@@ -278,6 +412,7 @@ import { downloadMasterDatabase } from 'shamela';
278
412
  outputFile: { path: './shamela_master.json' },
279
413
  });
280
414
  console.log(`Master data exported to: ${jsonPath}`);
415
+ console.log('The JSON file includes authors, books, categories, and the master version number.');
281
416
  } catch (error) {
282
417
  console.error('Error downloading master database:', error);
283
418
  }
@@ -339,6 +474,24 @@ import { getBook } from 'shamela';
339
474
  })();
340
475
  ```
341
476
 
477
+ ### Retrieving Master Data in memory
478
+
479
+ ```javascript
480
+ import { getMaster } from 'shamela';
481
+
482
+ (async () => {
483
+ try {
484
+ const masterData = await getMaster();
485
+
486
+ console.log(`Master snapshot version: ${masterData.version}`);
487
+ console.log(`Master dataset includes ${masterData.books.length} books`);
488
+ console.log(`Master dataset includes ${masterData.categories.length} categories`);
489
+ } catch (error) {
490
+ console.error('Error retrieving master data:', error);
491
+ }
492
+ })();
493
+ ```
494
+
342
495
  ### Getting Book Cover URLs
343
496
 
344
497
  ```javascript
@@ -379,6 +532,7 @@ The library provides comprehensive TypeScript types for all data structures:
379
532
  - `authors`: Raw entries from the `author` table with the original `biography`, `death_text`, `death_number`, `is_deleted`, and `name` fields.
380
533
  - `books`: Raw entries from the `book` table containing the original metadata columns (`author`, `bibliography`, `category`, `date`, `hint`, `major_release`, `metadata`, `minor_release`, `pdf_links`, `printed`, `type`, and `is_deleted`).
381
534
  - `categories`: Raw entries from the `category` table including `is_deleted`, `order`, and `name`.
535
+ - `version`: Version number reported by the Shamela API for the downloaded master database.
382
536
 
383
537
  ### Page
384
538
 
@@ -401,12 +555,112 @@ The library provides comprehensive TypeScript types for all data structures:
401
555
  - `parseContentRobust(content: string)`: Converts Shamela page HTML into a list of structured lines while preserving title markers and punctuation.
402
556
  - `sanitizePageContent(content: string)`: Removes common footnote markers and normalises ligatures from Shamela pages.
403
557
 
558
+ ## Next.js demo
559
+
560
+ A minimal Next.js 16 application in `demo/` replaces the previous Storybook setup and offers an RTL-friendly explorer for the Shamela APIs. The server renders requests so the browser can bypass CORS limits and you only need to provide an API key and book identifier at runtime.
561
+
562
+ Create a `demo/.env.local` file (or export the variables in your shell) containing the real endpoints you wish to call:
563
+
564
+ ```dotenv
565
+ SHAMELA_API_MASTER_PATCH_ENDPOINT=https://dev.shamela.ws/api/v1/patches/master
566
+ SHAMELA_API_BOOKS_ENDPOINT=https://dev.shamela.ws/api/v1/patches/book-updates
567
+ # Optional when hosting the wasm asset yourself
568
+ # SHAMELA_SQLJS_WASM_URL=https://example.com/sql-wasm.wasm
569
+ ```
570
+
571
+ Then launch the demo:
572
+
573
+ ```bash
574
+ bun run demo
575
+ ```
576
+
577
+ Visit [http://localhost:3000](http://localhost:3000) to enter your API key, choose a book ID, and call helpers like `getMasterMetadata`, `getMaster`, `getBook`, and `downloadMasterDatabase` directly from the interface. For production-style builds use:
578
+
579
+ ```bash
580
+ bun run demo:build
581
+ bun run demo:start
582
+ ```
583
+
584
+ When deploying to Vercel, point the project to the `demo` directory and supply the same environment variables in the dashboard so the API routes can reach Shamela.
585
+
586
+ ## Troubleshooting
587
+
588
+ ### Error: "Unable to locate sql-wasm.wasm file"
589
+
590
+ This error occurs when the library cannot automatically find the WASM file. This is common in bundled environments like Next.js with Turbopack/Webpack.
591
+
592
+ **Solution:** Use the `createNodeConfig` helper or explicitly configure `sqlJsWasmUrl`:
593
+
594
+ ```typescript
595
+ import { configure, createNodeConfig } from 'shamela';
596
+
597
+ // Option 1: Use the helper (recommended)
598
+ configure(createNodeConfig({
599
+ apiKey: process.env.SHAMELA_API_KEY,
600
+ booksEndpoint: process.env.SHAMELA_BOOKS_ENDPOINT,
601
+ masterPatchEndpoint: process.env.SHAMELA_MASTER_ENDPOINT,
602
+ }));
603
+
604
+ // Option 2: Manual configuration
605
+ import { join } from 'node:path';
606
+
607
+ configure({
608
+ sqlJsWasmUrl: join(process.cwd(), 'node_modules', 'sql.js', 'dist', 'sql-wasm.wasm'),
609
+ apiKey: process.env.SHAMELA_API_KEY,
610
+ // ... other config
611
+ });
612
+ ```
613
+
614
+ ### Error: "ENOENT: no such file or directory, open 'https://...'"
615
+
616
+ This means you're in a Node.js environment but providing an HTTPS URL for the WASM file. Node.js requires a filesystem path, not a URL.
617
+
618
+ **Solution:** Use a filesystem path instead of a URL:
619
+
620
+ ```typescript
621
+ // ❌ Wrong - HTTPS URL in Node.js
622
+ configure({
623
+ sqlJsWasmUrl: 'https://cdn.jsdelivr.net/npm/sql.js@1.13.0/dist/sql-wasm.wasm'
624
+ });
625
+
626
+ // ✅ Correct - Filesystem path or use createNodeConfig
627
+ import { createNodeConfig } from 'shamela';
628
+
629
+ configure(createNodeConfig({
630
+ apiKey: process.env.SHAMELA_API_KEY,
631
+ // ... other config
632
+ }));
633
+ ```
634
+
635
+ ### Next.js: Module not found errors during build
636
+
637
+ If you see webpack/Turbopack errors about not being able to resolve modules during the build phase:
638
+
639
+ 1. Make sure you've added `serverExternalPackages` to your `next.config.js` (see [Next.js / Bundled Environments](#nextjs--bundled-environments))
640
+ 2. Ensure you're only importing shamela in server-side code (Server Actions, API Routes, Server Components)
641
+ 3. Never import shamela in `layout.tsx` or client components
642
+ 4. Create a separate `lib/shamela-server.ts` file for configuration
643
+
644
+ ### Double `node_modules/node_modules` path error
645
+
646
+ If you see paths like `/path/to/project/node_modules/node_modules/sql.js/...`, this indicates the library's auto-detection failed due to bundling. Use explicit configuration:
647
+
648
+ ```typescript
649
+ import { configure, createNodeConfig } from 'shamela';
650
+
651
+ configure(createNodeConfig({
652
+ apiKey: process.env.SHAMELA_API_KEY,
653
+ booksEndpoint: process.env.SHAMELA_BOOKS_ENDPOINT,
654
+ masterPatchEndpoint: process.env.SHAMELA_MASTER_ENDPOINT,
655
+ }));
656
+ ```
657
+
404
658
  ## Testing
405
659
 
406
- The library includes comprehensive tests. To run them, ensure you have the necessary environment variables set, then execute:
660
+ The library includes comprehensive tests powered by `bun test`. To run the unit suite, ensure you have the necessary environment variables set, then execute:
407
661
 
408
662
  ```bash
409
- bun test
663
+ bun test src
410
664
  ```
411
665
 
412
666
  For end-to-end tests:
@@ -415,10 +669,12 @@ For end-to-end tests:
415
669
  bun run e2e
416
670
  ```
417
671
 
418
- For CI environment:
672
+ ### Formatting
673
+
674
+ Apply Biome formatting across the repository with:
419
675
 
420
676
  ```bash
421
- bun run e2e:ci
677
+ bun run format
422
678
  ```
423
679
 
424
680
  ## License