@ashdev/codex-plugin-metadata-openlibrary 1.9.3 → 1.10.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/dist/index.js +278 -193
- package/dist/index.js.map +4 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -348,6 +348,151 @@ function createLogger(options) {
|
|
|
348
348
|
|
|
349
349
|
// node_modules/@ashdev/codex-plugin-sdk/dist/server.js
|
|
350
350
|
import { createInterface } from "node:readline";
|
|
351
|
+
|
|
352
|
+
// node_modules/@ashdev/codex-plugin-sdk/dist/storage.js
|
|
353
|
+
var StorageError = class extends Error {
|
|
354
|
+
code;
|
|
355
|
+
data;
|
|
356
|
+
constructor(message, code, data) {
|
|
357
|
+
super(message);
|
|
358
|
+
this.code = code;
|
|
359
|
+
this.data = data;
|
|
360
|
+
this.name = "StorageError";
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
var PluginStorage = class {
|
|
364
|
+
nextId = 1;
|
|
365
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
366
|
+
writeFn;
|
|
367
|
+
/**
|
|
368
|
+
* Create a new storage client.
|
|
369
|
+
*
|
|
370
|
+
* @param writeFn - Optional custom write function (defaults to process.stdout.write).
|
|
371
|
+
* Useful for testing or custom transport layers.
|
|
372
|
+
*/
|
|
373
|
+
constructor(writeFn) {
|
|
374
|
+
this.writeFn = writeFn ?? ((line) => {
|
|
375
|
+
process.stdout.write(line);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Get a value by key
|
|
380
|
+
*
|
|
381
|
+
* @param key - Storage key to retrieve
|
|
382
|
+
* @returns The stored data and optional expiration, or null data if key doesn't exist
|
|
383
|
+
*/
|
|
384
|
+
async get(key) {
|
|
385
|
+
return await this.sendRequest("storage/get", { key });
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Set a value by key (upsert - creates or updates)
|
|
389
|
+
*
|
|
390
|
+
* @param key - Storage key
|
|
391
|
+
* @param data - JSON-serializable data to store
|
|
392
|
+
* @param expiresAt - Optional expiration timestamp (ISO 8601)
|
|
393
|
+
* @returns Success indicator
|
|
394
|
+
*/
|
|
395
|
+
async set(key, data, expiresAt) {
|
|
396
|
+
const params = { key, data };
|
|
397
|
+
if (expiresAt !== void 0) {
|
|
398
|
+
params.expiresAt = expiresAt;
|
|
399
|
+
}
|
|
400
|
+
return await this.sendRequest("storage/set", params);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Delete a value by key
|
|
404
|
+
*
|
|
405
|
+
* @param key - Storage key to delete
|
|
406
|
+
* @returns Whether the key existed and was deleted
|
|
407
|
+
*/
|
|
408
|
+
async delete(key) {
|
|
409
|
+
return await this.sendRequest("storage/delete", { key });
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* List all keys for this plugin instance (excluding expired)
|
|
413
|
+
*
|
|
414
|
+
* @returns List of key entries with metadata
|
|
415
|
+
*/
|
|
416
|
+
async list() {
|
|
417
|
+
return await this.sendRequest("storage/list", {});
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Clear all data for this plugin instance
|
|
421
|
+
*
|
|
422
|
+
* @returns Number of entries deleted
|
|
423
|
+
*/
|
|
424
|
+
async clear() {
|
|
425
|
+
return await this.sendRequest("storage/clear", {});
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Handle an incoming JSON-RPC response line from the host.
|
|
429
|
+
*
|
|
430
|
+
* Call this method from your readline handler to deliver responses
|
|
431
|
+
* back to pending storage requests.
|
|
432
|
+
*/
|
|
433
|
+
handleResponse(line) {
|
|
434
|
+
const trimmed = line.trim();
|
|
435
|
+
if (!trimmed)
|
|
436
|
+
return;
|
|
437
|
+
let parsed;
|
|
438
|
+
try {
|
|
439
|
+
parsed = JSON.parse(trimmed);
|
|
440
|
+
} catch {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const obj = parsed;
|
|
444
|
+
if (obj.method !== void 0) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const id = obj.id;
|
|
448
|
+
if (id === void 0 || id === null)
|
|
449
|
+
return;
|
|
450
|
+
const pending = this.pendingRequests.get(id);
|
|
451
|
+
if (!pending)
|
|
452
|
+
return;
|
|
453
|
+
this.pendingRequests.delete(id);
|
|
454
|
+
if ("error" in obj && obj.error) {
|
|
455
|
+
const err = obj.error;
|
|
456
|
+
pending.reject(new StorageError(err.message, err.code, err.data));
|
|
457
|
+
} else {
|
|
458
|
+
pending.resolve(obj.result);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Cancel all pending requests (e.g. on shutdown).
|
|
463
|
+
*/
|
|
464
|
+
cancelAll() {
|
|
465
|
+
for (const [, pending] of this.pendingRequests) {
|
|
466
|
+
pending.reject(new StorageError("Storage client stopped", -1));
|
|
467
|
+
}
|
|
468
|
+
this.pendingRequests.clear();
|
|
469
|
+
}
|
|
470
|
+
// ===========================================================================
|
|
471
|
+
// Internal
|
|
472
|
+
// ===========================================================================
|
|
473
|
+
sendRequest(method, params) {
|
|
474
|
+
const id = this.nextId++;
|
|
475
|
+
const request = {
|
|
476
|
+
jsonrpc: "2.0",
|
|
477
|
+
id,
|
|
478
|
+
method,
|
|
479
|
+
params
|
|
480
|
+
};
|
|
481
|
+
return new Promise((resolve, reject) => {
|
|
482
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
483
|
+
try {
|
|
484
|
+
this.writeFn(`${JSON.stringify(request)}
|
|
485
|
+
`);
|
|
486
|
+
} catch (err) {
|
|
487
|
+
this.pendingRequests.delete(id);
|
|
488
|
+
const message = err instanceof Error ? err.message : "Unknown write error";
|
|
489
|
+
reject(new StorageError(`Failed to send request: ${message}`, -1));
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// node_modules/@ashdev/codex-plugin-sdk/dist/server.js
|
|
351
496
|
function validateStringFields(params, fields) {
|
|
352
497
|
if (params === null || params === void 0) {
|
|
353
498
|
return { field: "params", message: "params is required" };
|
|
@@ -408,26 +553,22 @@ function invalidParamsError(id, error) {
|
|
|
408
553
|
}
|
|
409
554
|
};
|
|
410
555
|
}
|
|
411
|
-
function
|
|
412
|
-
const { manifest: manifest2,
|
|
556
|
+
function createPluginServer(options) {
|
|
557
|
+
const { manifest: manifest2, onInitialize, logLevel = "info", label, router } = options;
|
|
413
558
|
const logger2 = createLogger({ name: manifest2.name, level: logLevel });
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
if (contentTypes.includes("book") && !bookProvider2) {
|
|
419
|
-
throw new Error("Book metadata provider is required when 'book' is in metadataProvider capabilities");
|
|
420
|
-
}
|
|
421
|
-
logger2.info(`Starting plugin: ${manifest2.displayName} v${manifest2.version}`);
|
|
559
|
+
const prefix = label ? `${label} plugin` : "plugin";
|
|
560
|
+
const storage = new PluginStorage();
|
|
561
|
+
logger2.info(`Starting ${prefix}: ${manifest2.displayName} v${manifest2.version}`);
|
|
422
562
|
const rl = createInterface({
|
|
423
563
|
input: process.stdin,
|
|
424
564
|
terminal: false
|
|
425
565
|
});
|
|
426
566
|
rl.on("line", (line) => {
|
|
427
|
-
void handleLine(line, manifest2,
|
|
567
|
+
void handleLine(line, manifest2, onInitialize, router, logger2, storage);
|
|
428
568
|
});
|
|
429
569
|
rl.on("close", () => {
|
|
430
570
|
logger2.info("stdin closed, shutting down");
|
|
571
|
+
storage.cancelAll();
|
|
431
572
|
process.exit(0);
|
|
432
573
|
});
|
|
433
574
|
process.on("uncaughtException", (error) => {
|
|
@@ -438,16 +579,33 @@ function createMetadataPlugin(options) {
|
|
|
438
579
|
logger2.error("Unhandled rejection", reason);
|
|
439
580
|
});
|
|
440
581
|
}
|
|
441
|
-
|
|
582
|
+
function isJsonRpcResponse(obj) {
|
|
583
|
+
if (obj.method !== void 0)
|
|
584
|
+
return false;
|
|
585
|
+
if (obj.id === void 0 || obj.id === null)
|
|
586
|
+
return false;
|
|
587
|
+
return "result" in obj || "error" in obj;
|
|
588
|
+
}
|
|
589
|
+
async function handleLine(line, manifest2, onInitialize, router, logger2, storage) {
|
|
442
590
|
const trimmed = line.trim();
|
|
443
591
|
if (!trimmed)
|
|
444
592
|
return;
|
|
593
|
+
let parsed;
|
|
594
|
+
try {
|
|
595
|
+
parsed = JSON.parse(trimmed);
|
|
596
|
+
} catch {
|
|
597
|
+
}
|
|
598
|
+
if (parsed && isJsonRpcResponse(parsed)) {
|
|
599
|
+
logger2.debug("Routing storage response", { id: parsed.id });
|
|
600
|
+
storage.handleResponse(trimmed);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
445
603
|
let id = null;
|
|
446
604
|
try {
|
|
447
|
-
const request = JSON.parse(trimmed);
|
|
605
|
+
const request = parsed ?? JSON.parse(trimmed);
|
|
448
606
|
id = request.id;
|
|
449
607
|
logger2.debug(`Received request: ${request.method}`, { id: request.id });
|
|
450
|
-
const response = await handleRequest(request, manifest2,
|
|
608
|
+
const response = await handleRequest(request, manifest2, onInitialize, router, logger2, storage);
|
|
451
609
|
if (response !== null) {
|
|
452
610
|
writeResponse(response);
|
|
453
611
|
}
|
|
@@ -481,203 +639,130 @@ async function handleLine(line, manifest2, provider, bookProvider2, onInitialize
|
|
|
481
639
|
}
|
|
482
640
|
}
|
|
483
641
|
}
|
|
484
|
-
async function handleRequest(request, manifest2,
|
|
642
|
+
async function handleRequest(request, manifest2, onInitialize, router, logger2, storage) {
|
|
485
643
|
const { method, params, id } = request;
|
|
486
644
|
switch (method) {
|
|
487
|
-
case "initialize":
|
|
645
|
+
case "initialize": {
|
|
646
|
+
const initParams = params ?? {};
|
|
647
|
+
initParams.storage = storage;
|
|
488
648
|
if (onInitialize) {
|
|
489
|
-
await onInitialize(
|
|
649
|
+
await onInitialize(initParams);
|
|
490
650
|
}
|
|
491
|
-
return {
|
|
492
|
-
|
|
493
|
-
id,
|
|
494
|
-
result: manifest2
|
|
495
|
-
};
|
|
651
|
+
return { jsonrpc: "2.0", id, result: manifest2 };
|
|
652
|
+
}
|
|
496
653
|
case "ping":
|
|
497
|
-
return {
|
|
498
|
-
jsonrpc: "2.0",
|
|
499
|
-
id,
|
|
500
|
-
result: "pong"
|
|
501
|
-
};
|
|
654
|
+
return { jsonrpc: "2.0", id, result: "pong" };
|
|
502
655
|
case "shutdown": {
|
|
503
656
|
logger2.info("Shutdown requested");
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
result: null
|
|
508
|
-
};
|
|
509
|
-
process.stdout.write(`${JSON.stringify(response)}
|
|
657
|
+
storage.cancelAll();
|
|
658
|
+
const response2 = { jsonrpc: "2.0", id, result: null };
|
|
659
|
+
process.stdout.write(`${JSON.stringify(response2)}
|
|
510
660
|
`, () => {
|
|
511
661
|
process.exit(0);
|
|
512
662
|
});
|
|
513
663
|
return null;
|
|
514
664
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
const validationError = validateSearchParams(params);
|
|
530
|
-
if (validationError) {
|
|
531
|
-
return invalidParamsError(id, validationError);
|
|
532
|
-
}
|
|
533
|
-
return {
|
|
534
|
-
jsonrpc: "2.0",
|
|
535
|
-
id,
|
|
536
|
-
result: await provider.search(params)
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
case "metadata/series/get": {
|
|
540
|
-
if (!provider) {
|
|
541
|
-
return {
|
|
542
|
-
jsonrpc: "2.0",
|
|
543
|
-
id,
|
|
544
|
-
error: {
|
|
545
|
-
code: JSON_RPC_ERROR_CODES.METHOD_NOT_FOUND,
|
|
546
|
-
message: "This plugin does not support series metadata"
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
const validationError = validateGetParams(params);
|
|
551
|
-
if (validationError) {
|
|
552
|
-
return invalidParamsError(id, validationError);
|
|
553
|
-
}
|
|
554
|
-
return {
|
|
555
|
-
jsonrpc: "2.0",
|
|
556
|
-
id,
|
|
557
|
-
result: await provider.get(params)
|
|
558
|
-
};
|
|
665
|
+
}
|
|
666
|
+
const response = await router(method, params, id);
|
|
667
|
+
if (response !== null) {
|
|
668
|
+
return response;
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
jsonrpc: "2.0",
|
|
672
|
+
id,
|
|
673
|
+
error: {
|
|
674
|
+
code: JSON_RPC_ERROR_CODES.METHOD_NOT_FOUND,
|
|
675
|
+
message: `Method not found: ${method}`
|
|
559
676
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
jsonrpc: "2.0",
|
|
574
|
-
id,
|
|
575
|
-
error: {
|
|
576
|
-
code: JSON_RPC_ERROR_CODES.METHOD_NOT_FOUND,
|
|
577
|
-
message: "This plugin does not support series match"
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
const validationError = validateMatchParams(params);
|
|
582
|
-
if (validationError) {
|
|
583
|
-
return invalidParamsError(id, validationError);
|
|
584
|
-
}
|
|
585
|
-
return {
|
|
586
|
-
jsonrpc: "2.0",
|
|
587
|
-
id,
|
|
588
|
-
result: await provider.match(params)
|
|
589
|
-
};
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
function writeResponse(response) {
|
|
680
|
+
process.stdout.write(`${JSON.stringify(response)}
|
|
681
|
+
`);
|
|
682
|
+
}
|
|
683
|
+
function methodNotFound(id, message) {
|
|
684
|
+
return {
|
|
685
|
+
jsonrpc: "2.0",
|
|
686
|
+
id,
|
|
687
|
+
error: {
|
|
688
|
+
code: JSON_RPC_ERROR_CODES.METHOD_NOT_FOUND,
|
|
689
|
+
message
|
|
590
690
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
function success(id, result) {
|
|
694
|
+
return { jsonrpc: "2.0", id, result };
|
|
695
|
+
}
|
|
696
|
+
function createMetadataPlugin(options) {
|
|
697
|
+
const { manifest: manifest2, provider, bookProvider: bookProvider2, onInitialize, logLevel } = options;
|
|
698
|
+
const contentTypes = manifest2.capabilities.metadataProvider;
|
|
699
|
+
if (contentTypes.includes("series") && !provider) {
|
|
700
|
+
throw new Error("Series metadata provider is required when 'series' is in metadataProvider capabilities");
|
|
701
|
+
}
|
|
702
|
+
if (contentTypes.includes("book") && !bookProvider2) {
|
|
703
|
+
throw new Error("Book metadata provider is required when 'book' is in metadataProvider capabilities");
|
|
704
|
+
}
|
|
705
|
+
const router = async (method, params, id) => {
|
|
706
|
+
switch (method) {
|
|
707
|
+
// Series metadata methods
|
|
708
|
+
case "metadata/series/search": {
|
|
709
|
+
if (!provider)
|
|
710
|
+
return methodNotFound(id, "This plugin does not support series metadata");
|
|
711
|
+
const err = validateSearchParams(params);
|
|
712
|
+
if (err)
|
|
713
|
+
return invalidParamsError(id, err);
|
|
714
|
+
return success(id, await provider.search(params));
|
|
608
715
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
if (!bookProvider2) {
|
|
617
|
-
return {
|
|
618
|
-
jsonrpc: "2.0",
|
|
619
|
-
id,
|
|
620
|
-
error: {
|
|
621
|
-
code: JSON_RPC_ERROR_CODES.METHOD_NOT_FOUND,
|
|
622
|
-
message: "This plugin does not support book metadata"
|
|
623
|
-
}
|
|
624
|
-
};
|
|
716
|
+
case "metadata/series/get": {
|
|
717
|
+
if (!provider)
|
|
718
|
+
return methodNotFound(id, "This plugin does not support series metadata");
|
|
719
|
+
const err = validateGetParams(params);
|
|
720
|
+
if (err)
|
|
721
|
+
return invalidParamsError(id, err);
|
|
722
|
+
return success(id, await provider.get(params));
|
|
625
723
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
724
|
+
case "metadata/series/match": {
|
|
725
|
+
if (!provider)
|
|
726
|
+
return methodNotFound(id, "This plugin does not support series metadata");
|
|
727
|
+
if (!provider.match)
|
|
728
|
+
return methodNotFound(id, "This plugin does not support series match");
|
|
729
|
+
const err = validateMatchParams(params);
|
|
730
|
+
if (err)
|
|
731
|
+
return invalidParamsError(id, err);
|
|
732
|
+
return success(id, await provider.match(params));
|
|
629
733
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
return {
|
|
639
|
-
jsonrpc: "2.0",
|
|
640
|
-
id,
|
|
641
|
-
error: {
|
|
642
|
-
code: JSON_RPC_ERROR_CODES.METHOD_NOT_FOUND,
|
|
643
|
-
message: "This plugin does not support book metadata"
|
|
644
|
-
}
|
|
645
|
-
};
|
|
734
|
+
// Book metadata methods
|
|
735
|
+
case "metadata/book/search": {
|
|
736
|
+
if (!bookProvider2)
|
|
737
|
+
return methodNotFound(id, "This plugin does not support book metadata");
|
|
738
|
+
const err = validateBookSearchParams(params);
|
|
739
|
+
if (err)
|
|
740
|
+
return invalidParamsError(id, err);
|
|
741
|
+
return success(id, await bookProvider2.search(params));
|
|
646
742
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
}
|
|
655
|
-
};
|
|
743
|
+
case "metadata/book/get": {
|
|
744
|
+
if (!bookProvider2)
|
|
745
|
+
return methodNotFound(id, "This plugin does not support book metadata");
|
|
746
|
+
const err = validateGetParams(params);
|
|
747
|
+
if (err)
|
|
748
|
+
return invalidParamsError(id, err);
|
|
749
|
+
return success(id, await bookProvider2.get(params));
|
|
656
750
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
751
|
+
case "metadata/book/match": {
|
|
752
|
+
if (!bookProvider2)
|
|
753
|
+
return methodNotFound(id, "This plugin does not support book metadata");
|
|
754
|
+
if (!bookProvider2.match)
|
|
755
|
+
return methodNotFound(id, "This plugin does not support book match");
|
|
756
|
+
const err = validateBookMatchParams(params);
|
|
757
|
+
if (err)
|
|
758
|
+
return invalidParamsError(id, err);
|
|
759
|
+
return success(id, await bookProvider2.match(params));
|
|
660
760
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
id,
|
|
664
|
-
result: await bookProvider2.match(params)
|
|
665
|
-
};
|
|
761
|
+
default:
|
|
762
|
+
return null;
|
|
666
763
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
jsonrpc: "2.0",
|
|
670
|
-
id,
|
|
671
|
-
error: {
|
|
672
|
-
code: JSON_RPC_ERROR_CODES.METHOD_NOT_FOUND,
|
|
673
|
-
message: `Method not found: ${method}`
|
|
674
|
-
}
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
function writeResponse(response) {
|
|
679
|
-
process.stdout.write(`${JSON.stringify(response)}
|
|
680
|
-
`);
|
|
764
|
+
};
|
|
765
|
+
createPluginServer({ manifest: manifest2, onInitialize, logLevel, router });
|
|
681
766
|
}
|
|
682
767
|
|
|
683
768
|
// src/index.ts
|
|
@@ -686,7 +771,7 @@ init_api();
|
|
|
686
771
|
// package.json
|
|
687
772
|
var package_default = {
|
|
688
773
|
name: "@ashdev/codex-plugin-metadata-openlibrary",
|
|
689
|
-
version: "1.
|
|
774
|
+
version: "1.10.0",
|
|
690
775
|
description: "Open Library metadata plugin for Codex - fetches book metadata by ISBN or title search",
|
|
691
776
|
main: "dist/index.js",
|
|
692
777
|
bin: "dist/index.js",
|
|
@@ -726,7 +811,7 @@ var package_default = {
|
|
|
726
811
|
node: ">=22.0.0"
|
|
727
812
|
},
|
|
728
813
|
dependencies: {
|
|
729
|
-
"@ashdev/codex-plugin-sdk": "^1.
|
|
814
|
+
"@ashdev/codex-plugin-sdk": "^1.10.0"
|
|
730
815
|
},
|
|
731
816
|
devDependencies: {
|
|
732
817
|
"@biomejs/biome": "^2.3.13",
|
|
@@ -1188,7 +1273,7 @@ createMetadataPlugin({
|
|
|
1188
1273
|
bookProvider,
|
|
1189
1274
|
logLevel: "info",
|
|
1190
1275
|
onInitialize(params) {
|
|
1191
|
-
const maxResults = params.
|
|
1276
|
+
const maxResults = params.adminConfig?.maxResults;
|
|
1192
1277
|
if (maxResults !== void 0) {
|
|
1193
1278
|
config.maxResults = Math.min(Math.max(1, maxResults), 50);
|
|
1194
1279
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/api.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/types/rpc.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/errors.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/logger.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/server.ts", "../src/index.ts", "../package.json", "../src/manifest.ts", "../src/mapper.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Open Library API Client\n *\n * Handles communication with the Open Library API with:\n * - Rate limiting (100 requests per 5 minutes recommended)\n * - Caching to reduce API calls\n * - Error handling with retries\n *\n * @see https://openlibrary.org/developers/api\n */\n\nimport type {\n OLAuthor,\n OLEdition,\n OLSearchResponse,\n OLWork,\n OLWorkEditionsResponse,\n} from \"./types.js\";\n\nconst BASE_URL = \"https://openlibrary.org\";\nconst COVERS_BASE_URL = \"https://covers.openlibrary.org\";\n\n// Simple in-memory cache with TTL\ninterface CacheEntry<T> {\n data: T;\n timestamp: number;\n}\n\nconst CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes\nconst cache = new Map<string, CacheEntry<unknown>>();\n\n/**\n * Get cached value if not expired\n */\nfunction getCached<T>(key: string): T | null {\n const entry = cache.get(key);\n if (entry && Date.now() - entry.timestamp < CACHE_TTL_MS) {\n return entry.data as T;\n }\n if (entry) {\n cache.delete(key); // Cleanup expired\n }\n return null;\n}\n\n/**\n * Store value in cache\n */\nfunction setCache<T>(key: string, data: T): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n/**\n * Make an HTTP request with error handling\n */\nasync function fetchJson<T>(url: string, description: string): Promise<T | null> {\n // Check cache first\n const cached = getCached<T>(url);\n if (cached !== null) {\n return cached;\n }\n\n try {\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": \"Codex/1.0 (https://github.com/AshDevFr/codex; codex-plugin)\",\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data = (await response.json()) as T;\n setCache(url, data);\n return data;\n } catch (error) {\n console.error(`[openlibrary] Failed to fetch ${description}:`, error);\n return null;\n }\n}\n\n/**\n * Normalize ISBN by removing hyphens and spaces\n */\nexport function normalizeIsbn(isbn: string): string {\n return isbn.replace(/[-\\s]/g, \"\").toUpperCase();\n}\n\n/**\n * Check if a string is a valid ISBN-10 or ISBN-13\n */\nexport function isValidIsbn(isbn: string): boolean {\n const normalized = normalizeIsbn(isbn);\n return normalized.length === 10 || normalized.length === 13;\n}\n\n/**\n * Fetch book edition by ISBN\n *\n * @param isbn ISBN-10 or ISBN-13\n * @returns Edition data or null if not found\n */\nexport async function getEditionByIsbn(isbn: string): Promise<OLEdition | null> {\n const normalized = normalizeIsbn(isbn);\n const url = `${BASE_URL}/isbn/${normalized}.json`;\n return fetchJson<OLEdition>(url, `edition by ISBN ${normalized}`);\n}\n\n/**\n * Fetch work details by key\n *\n * @param workKey Work key (e.g., \"/works/OL45883W\" or just \"OL45883W\")\n * @returns Work data or null if not found\n */\nexport async function getWork(workKey: string): Promise<OLWork | null> {\n // Normalize key to just the ID part\n const key = workKey.startsWith(\"/works/\") ? workKey : `/works/${workKey}`;\n const url = `${BASE_URL}${key}.json`;\n return fetchJson<OLWork>(url, `work ${key}`);\n}\n\n/**\n * Fetch editions for a work\n *\n * Returns editions directly associated with a work, ordered by most recent.\n * This is more reliable than searching by title, which can return unrelated books.\n *\n * @param workKey Work key (e.g., \"/works/OL45883W\")\n * @param limit Maximum number of editions to fetch\n * @returns Array of editions or empty array if none found\n */\nexport async function getWorkEditions(workKey: string, limit = 5): Promise<OLEdition[]> {\n const key = workKey.startsWith(\"/works/\") ? workKey : `/works/${workKey}`;\n const url = `${BASE_URL}${key}/editions.json?limit=${limit}`;\n const response = await fetchJson<OLWorkEditionsResponse>(url, `editions for ${key}`);\n return response?.entries || [];\n}\n\n/**\n * Fetch author details by key\n *\n * @param authorKey Author key (e.g., \"/authors/OL34184A\" or just \"OL34184A\")\n * @returns Author data or null if not found\n */\nexport async function getAuthor(authorKey: string): Promise<OLAuthor | null> {\n // Normalize key to just the ID part\n const key = authorKey.startsWith(\"/authors/\") ? authorKey : `/authors/${authorKey}`;\n const url = `${BASE_URL}${key}.json`;\n return fetchJson<OLAuthor>(url, `author ${key}`);\n}\n\n/** Fields to request from the Open Library search API */\nconst SEARCH_FIELDS = [\n \"key\",\n \"title\",\n \"subtitle\",\n \"author_name\",\n \"author_key\",\n \"first_publish_year\",\n \"publish_year\",\n \"publisher\",\n \"isbn\",\n \"number_of_pages_median\",\n \"cover_i\",\n \"cover_edition_key\",\n \"edition_count\",\n \"language\",\n \"subject\",\n \"ratings_average\",\n \"ratings_count\",\n].join(\",\");\n\n/**\n * Search for books\n *\n * When an author is provided, uses the `title` + `author` parameters for\n * more precise results. If that yields no results, falls back to a general\n * `q` search to ensure we still return something useful.\n *\n * @param query Search query (title, author, or combined)\n * @param options Additional search options\n * @returns Search results\n */\nexport async function searchBooks(\n query: string,\n options: {\n author?: string;\n limit?: number;\n } = {},\n): Promise<OLSearchResponse | null> {\n const { author, limit = 10 } = options;\n\n // When author is provided, try a refined title + author search first\n if (author) {\n const params = new URLSearchParams({\n title: query,\n author,\n fields: SEARCH_FIELDS,\n limit: String(limit),\n });\n\n const url = `${BASE_URL}/search.json?${params}`;\n const response = await fetchJson<OLSearchResponse>(\n url,\n `search title=\"${query}\" author=\"${author}\"`,\n );\n\n if (response?.docs?.length) {\n return response;\n }\n\n // Fall back to general q search if title+author yielded no results\n }\n\n // General search using q parameter\n const params = new URLSearchParams({\n q: query,\n fields: SEARCH_FIELDS,\n limit: String(limit),\n });\n\n if (author) {\n params.set(\"author\", author);\n }\n\n const url = `${BASE_URL}/search.json?${params}`;\n return fetchJson<OLSearchResponse>(url, `search \"${query}\"`);\n}\n\n/**\n * Get cover image URL by ISBN\n *\n * @param isbn ISBN-10 or ISBN-13\n * @param size Cover size: S (small ~50w), M (medium ~180w), L (large ~300w+)\n * @returns Cover URL\n */\nexport function getCoverUrlByIsbn(isbn: string, size: \"S\" | \"M\" | \"L\"): string {\n const normalized = normalizeIsbn(isbn);\n return `${COVERS_BASE_URL}/b/isbn/${normalized}-${size}.jpg`;\n}\n\n/**\n * Get cover image URL by cover ID\n *\n * @param coverId Open Library cover ID\n * @param size Cover size: S (small), M (medium), L (large)\n * @returns Cover URL\n */\nexport function getCoverUrlById(coverId: number, size: \"S\" | \"M\" | \"L\"): string {\n return `${COVERS_BASE_URL}/b/id/${coverId}-${size}.jpg`;\n}\n\n/**\n * Get cover image URL by Open Library ID (OLID)\n *\n * @param olid Open Library ID (e.g., \"OL7353617M\" for edition, \"OL45883W\" for work)\n * @param size Cover size: S (small), M (medium), L (large)\n * @returns Cover URL\n */\nexport function getCoverUrlByOlid(olid: string, size: \"S\" | \"M\" | \"L\"): string {\n // Strip any prefix if present\n const id = olid.replace(/^\\/(?:books|works)\\//, \"\");\n return `${COVERS_BASE_URL}/b/olid/${id}-${size}.jpg`;\n}\n\n/**\n * Parse year from Open Library date string\n *\n * Open Library dates can be in various formats:\n * - \"2020\"\n * - \"January 1, 2020\"\n * - \"2020-01-15\"\n * - \"c1985\"\n * - \"1985?\"\n *\n * @param dateStr Date string from Open Library\n * @returns Parsed year or undefined if unable to parse\n */\nexport function parseYear(dateStr: string | undefined): number | undefined {\n if (!dateStr) return undefined;\n\n // Try to extract a 4-digit year\n // Using (?:^|[^0-9]) to handle \"c1985\" format where there's no word boundary\n const match = dateStr.match(/(?:^|[^0-9])(1[89]\\d{2}|20\\d{2})(?:[^0-9]|$)/);\n if (match) {\n return Number.parseInt(match[1], 10);\n }\n\n return undefined;\n}\n\n/**\n * Parse description from Open Library\n *\n * Description can be either a string or an object with { type, value }.\n * Strips HTML tags and normalizes whitespace, since Open Library descriptions\n * can contain raw HTML (e.g., from Standard Ebooks imports).\n */\nexport function parseDescription(\n desc: string | { type?: string; value: string } | undefined,\n): string | undefined {\n if (!desc) return undefined;\n const raw = typeof desc === \"string\" ? desc : desc.value;\n return stripHtml(raw);\n}\n\n/**\n * Strip HTML tags from a string and normalize whitespace.\n *\n * Converts block-level tags (p, br, div, li) to newlines,\n * strips all remaining tags, decodes common HTML entities,\n * and collapses excessive whitespace.\n */\nfunction stripHtml(html: string): string | undefined {\n let text = html;\n\n // Convert block-level elements to newlines\n text = text.replace(/<\\/(p|div|li|tr|h[1-6])>/gi, \"\\n\");\n text = text.replace(/<br\\s*\\/?>/gi, \"\\n\");\n\n // Remove all remaining HTML tags\n text = text.replace(/<[^>]+>/g, \"\");\n\n // Decode common HTML entities\n text = text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n .replace(/ /g, \" \");\n\n // Collapse multiple spaces/tabs on the same line into one space\n text = text.replace(/[^\\S\\n]+/g, \" \");\n\n // Collapse 3+ consecutive newlines into 2\n text = text.replace(/\\n{3,}/g, \"\\n\\n\");\n\n // Trim each line and remove leading/trailing whitespace\n text = text\n .split(\"\\n\")\n .map((line) => line.trim())\n .join(\"\\n\")\n .trim();\n\n return text || undefined;\n}\n\n/**\n * Convert Open Library language code to BCP47\n *\n * Open Library uses format like \"/languages/eng\"\n *\n * @param langRef Language reference (e.g., \"/languages/eng\")\n * @returns BCP47 language code (e.g., \"en\")\n */\nexport function parseLanguage(langRef: string | undefined): string | undefined {\n if (!langRef) return undefined;\n\n // Extract language code from \"/languages/xxx\" format\n const match = langRef.match(/\\/languages\\/(\\w+)$/);\n if (!match) return undefined;\n\n const code = match[1].toLowerCase();\n\n // Map Open Library 3-letter codes to BCP47 2-letter codes\n const languageMap: Record<string, string> = {\n eng: \"en\",\n spa: \"es\",\n fre: \"fr\",\n fra: \"fr\",\n ger: \"de\",\n deu: \"de\",\n ita: \"it\",\n por: \"pt\",\n rus: \"ru\",\n jpn: \"ja\",\n chi: \"zh\",\n zho: \"zh\",\n kor: \"ko\",\n ara: \"ar\",\n hin: \"hi\",\n pol: \"pl\",\n tur: \"tr\",\n dut: \"nl\",\n nld: \"nl\",\n swe: \"sv\",\n nor: \"no\",\n dan: \"da\",\n fin: \"fi\",\n cze: \"cs\",\n ces: \"cs\",\n gre: \"el\",\n ell: \"el\",\n heb: \"he\",\n hun: \"hu\",\n rom: \"ro\",\n ron: \"ro\",\n tha: \"th\",\n vie: \"vi\",\n ind: \"id\",\n mal: \"ms\",\n msa: \"ms\",\n ukr: \"uk\",\n cat: \"ca\",\n lat: \"la\",\n };\n\n return languageMap[code] || code;\n}\n\n/**\n * Extract Open Library ID from a key\n *\n * @param key Full key (e.g., \"/works/OL45883W\" or \"/books/OL7353617M\")\n * @returns Just the ID (e.g., \"OL45883W\" or \"OL7353617M\")\n */\nexport function extractOlid(key: string): string {\n return key.replace(/^\\/(?:works|books|authors)\\//, \"\");\n}\n\n/**\n * Build Open Library URL from a key\n *\n * @param key Key (e.g., \"/works/OL45883W\")\n * @returns Full URL (e.g., \"https://openlibrary.org/works/OL45883W\")\n */\nexport function buildOpenLibraryUrl(key: string): string {\n return `${BASE_URL}${key.startsWith(\"/\") ? key : `/${key}`}`;\n}\n\n/**\n * Clear the cache\n */\nexport function clearCache(): void {\n cache.clear();\n}\n", null, null, null, null, "/**\n * Open Library Metadata Plugin for Codex\n *\n * Fetches book metadata from Open Library (openlibrary.org), a free and open\n * book database with extensive ISBN coverage.\n *\n * Features:\n * - ISBN lookup for direct, accurate matching\n * - Title/author search for fuzzy matching\n * - Cover image fetching in multiple sizes\n * - Author resolution with proper names\n * - Subject/genre extraction\n *\n * @see https://openlibrary.org/developers/api\n */\n\nimport {\n type BookMatchParams,\n type BookMetadataProvider,\n type BookSearchParams,\n createLogger,\n createMetadataPlugin,\n type InitializeParams,\n type MetadataGetParams,\n type MetadataMatchResponse,\n type MetadataSearchResponse,\n type PluginBookMetadata,\n} from \"@ashdev/codex-plugin-sdk\";\n\nimport { getEditionByIsbn, getWork, isValidIsbn, searchBooks } from \"./api.js\";\nimport { DEFAULT_MAX_RESULTS, manifest } from \"./manifest.js\";\nimport {\n getFullBookMetadata,\n mapEditionToBookMetadata,\n mapSearchDocToSearchResult,\n} from \"./mapper.js\";\n\nconst logger = createLogger({ name: \"openlibrary\", level: \"info\" });\n\n// Plugin configuration (set during initialization)\nconst config = {\n maxResults: DEFAULT_MAX_RESULTS,\n};\n\n/**\n * Book metadata provider implementation\n */\nconst bookProvider: BookMetadataProvider = {\n /**\n * Search for books by ISBN or title/author query\n *\n * If ISBN is provided, it takes priority for direct lookup.\n * Otherwise, falls back to title/author search.\n */\n async search(params: BookSearchParams): Promise<MetadataSearchResponse> {\n const { isbn, query, author, limit } = params;\n const maxResults = Math.min(limit || config.maxResults, 50);\n\n // If ISBN is provided, try direct lookup first\n if (isbn && isValidIsbn(isbn)) {\n const edition = await getEditionByIsbn(isbn);\n\n if (edition) {\n // Found by ISBN - return as single result with high relevance\n const workKey = edition.works?.[0]?.key;\n const workData = workKey ? await getWork(workKey) : null;\n const metadata = await mapEditionToBookMetadata(edition, workData);\n\n return {\n results: [\n {\n externalId: metadata.externalId,\n title: metadata.title || \"Unknown\",\n alternateTitles: metadata.subtitle ? [metadata.subtitle] : [],\n year: metadata.year,\n coverUrl: metadata.coverUrl,\n relevanceScore: 1.0, // Perfect match by ISBN\n preview: {\n genres: metadata.subjects.slice(0, 5),\n authors: metadata.authors.map((a) => a.name),\n },\n },\n ],\n };\n }\n\n // ISBN not found, fall through to search if query is also provided\n if (!query) {\n return { results: [] };\n }\n }\n\n // Title/author search\n if (!query) {\n return { results: [] };\n }\n\n const searchResponse = await searchBooks(query, {\n author,\n limit: maxResults,\n });\n\n if (!searchResponse?.docs?.length) {\n return { results: [] };\n }\n\n return {\n results: searchResponse.docs.map(mapSearchDocToSearchResult),\n };\n },\n\n /**\n * Get full book metadata by external ID\n *\n * The external ID can be:\n * - A work key: \"/works/OL45883W\"\n * - An edition key: \"/books/OL7353617M\"\n */\n async get(params: MetadataGetParams): Promise<PluginBookMetadata> {\n const { externalId } = params;\n\n // Try to get full metadata\n const metadata = await getFullBookMetadata(externalId);\n\n if (metadata) {\n return metadata;\n }\n\n // Fallback: return minimal metadata\n return {\n externalId,\n externalUrl: `https://openlibrary.org${externalId.startsWith(\"/\") ? externalId : `/${externalId}`}`,\n alternateTitles: [],\n isbns: [],\n genres: [],\n tags: [],\n subjects: [],\n authors: [],\n artists: [],\n covers: [],\n externalRatings: [],\n awards: [],\n externalLinks: [\n {\n url: `https://openlibrary.org${externalId.startsWith(\"/\") ? externalId : `/${externalId}`}`,\n label: \"Open Library\",\n linkType: \"provider\",\n },\n ],\n };\n },\n\n /**\n * Auto-match a book using available identifiers\n *\n * Match priority:\n * 1. ISBN (if provided) - highest confidence\n * 2. Title + author search - lower confidence\n */\n async match(params: BookMatchParams): Promise<MetadataMatchResponse> {\n const { title, authors, isbn, year } = params;\n\n // Try ISBN first if available\n if (isbn && isValidIsbn(isbn)) {\n const edition = await getEditionByIsbn(isbn);\n\n if (edition) {\n const workKey = edition.works?.[0]?.key;\n const workData = workKey ? await getWork(workKey) : null;\n const metadata = await mapEditionToBookMetadata(edition, workData);\n\n return {\n match: {\n externalId: metadata.externalId,\n title: metadata.title || \"Unknown\",\n alternateTitles: metadata.subtitle ? [metadata.subtitle] : [],\n year: metadata.year,\n coverUrl: metadata.coverUrl,\n relevanceScore: 1.0,\n preview: {\n genres: metadata.subjects.slice(0, 5),\n authors: metadata.authors.map((a) => a.name),\n },\n },\n confidence: 0.99, // Very high confidence for ISBN match\n alternatives: [],\n };\n }\n }\n\n // Fall back to title search\n const searchQuery = authors?.length ? `${title} ${authors[0]}` : title;\n\n const searchResponse = await searchBooks(searchQuery, {\n limit: 5,\n });\n\n if (!searchResponse?.docs?.length) {\n return {\n match: null,\n confidence: 0,\n alternatives: [],\n };\n }\n\n const results = searchResponse.docs.map(mapSearchDocToSearchResult);\n\n // Calculate confidence based on title similarity and other factors\n const bestMatch = results[0];\n let confidence = bestMatch.relevanceScore || 0.5;\n\n // Boost confidence if title matches closely\n const normalizedTitle = title.toLowerCase().trim();\n const normalizedMatchTitle = bestMatch.title.toLowerCase().trim();\n\n if (normalizedTitle === normalizedMatchTitle) {\n confidence = Math.min(1.0, confidence + 0.3);\n } else if (\n normalizedMatchTitle.includes(normalizedTitle) ||\n normalizedTitle.includes(normalizedMatchTitle)\n ) {\n confidence = Math.min(1.0, confidence + 0.15);\n }\n\n // Boost if year matches\n if (year && bestMatch.year === year) {\n confidence = Math.min(1.0, confidence + 0.1);\n }\n\n // Reduce confidence without ISBN\n confidence = Math.min(confidence, 0.85);\n\n return {\n match: bestMatch,\n confidence,\n alternatives: results.slice(1),\n };\n },\n};\n\n// =============================================================================\n// Plugin Initialization\n// =============================================================================\n\ncreateMetadataPlugin({\n manifest,\n bookProvider,\n logLevel: \"info\",\n onInitialize(params: InitializeParams) {\n // Read config from initialization params\n const maxResults = params.config?.maxResults as number | undefined;\n if (maxResults !== undefined) {\n config.maxResults = Math.min(Math.max(1, maxResults), 50); // Clamp 1-50\n }\n logger.info(`Plugin initialized (maxResults: ${config.maxResults})`);\n },\n});\n\nlogger.info(\"Open Library plugin started\");\n", "{\n \"name\": \"@ashdev/codex-plugin-metadata-openlibrary\",\n \"version\": \"1.9.3\",\n \"description\": \"Open Library metadata plugin for Codex - fetches book metadata by ISBN or title search\",\n \"main\": \"dist/index.js\",\n \"bin\": \"dist/index.js\",\n \"type\": \"module\",\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AshDevFr/codex.git\",\n \"directory\": \"plugins/metadata-openlibrary\"\n },\n \"scripts\": {\n \"build\": \"esbuild src/index.ts --bundle --platform=node --target=node22 --format=esm --outfile=dist/index.js --sourcemap --banner:js='#!/usr/bin/env node'\",\n \"dev\": \"npm run build -- --watch\",\n \"clean\": \"rm -rf dist\",\n \"start\": \"node dist/index.js\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"npm run lint && npm run build\"\n },\n \"keywords\": [\n \"codex\",\n \"plugin\",\n \"openlibrary\",\n \"metadata\",\n \"books\",\n \"isbn\"\n ],\n \"author\": \"Codex\",\n \"license\": \"MIT\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"dependencies\": {\n \"@ashdev/codex-plugin-sdk\": \"^1.9.3\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.3.13\",\n \"@types/node\": \"^22.0.0\",\n \"esbuild\": \"^0.24.0\",\n \"typescript\": \"^5.7.0\",\n \"vitest\": \"^3.0.0\"\n }\n}\n", "import type { MetadataContentType, PluginManifest } from \"@ashdev/codex-plugin-sdk\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\n// Default config values\nexport const DEFAULT_MAX_RESULTS = 10;\n\nexport const manifest = {\n name: \"metadata-openlibrary\",\n displayName: \"Open Library\",\n version: packageJson.version,\n description:\n \"Fetches book metadata from Open Library (openlibrary.org). Supports ISBN lookup and title search for EPUBs, PDFs, and other book formats.\",\n author: \"Codex\",\n homepage: \"https://openlibrary.org\",\n protocolVersion: \"1.0\",\n capabilities: {\n // Book metadata provider only (not series)\n metadataProvider: [\"book\"] as MetadataContentType[],\n },\n configSchema: {\n description: \"Configuration options for the Open Library plugin\",\n fields: [\n {\n key: \"maxResults\",\n label: \"Maximum Results\",\n description: \"Maximum number of results to return for search queries (1-50)\",\n type: \"number\" as const,\n required: false,\n default: DEFAULT_MAX_RESULTS,\n example: 20,\n },\n ],\n },\n} as const satisfies PluginManifest & {\n capabilities: { metadataProvider: MetadataContentType[] };\n};\n", "/**\n * Mapper functions to convert Open Library data to Codex plugin format\n */\n\nimport type {\n BookAuthor,\n BookCover,\n ExternalLink,\n ExternalRating,\n PluginBookMetadata,\n SearchResult,\n} from \"@ashdev/codex-plugin-sdk\";\n\nimport {\n buildOpenLibraryUrl,\n getAuthor,\n getCoverUrlById,\n getCoverUrlByIsbn,\n getWork,\n getWorkEditions,\n parseDescription,\n parseLanguage,\n parseYear,\n} from \"./api.js\";\nimport type { OLAuthorReference, OLEdition, OLSearchDoc, OLWork, ParsedAuthor } from \"./types.js\";\n\n/**\n * Map Open Library search result to Codex SearchResult\n */\nexport function mapSearchDocToSearchResult(doc: OLSearchDoc): SearchResult {\n const year = doc.first_publish_year;\n const coverUrl = doc.cover_i ? getCoverUrlById(doc.cover_i, \"M\") : undefined;\n\n // Calculate a relevance score based on available data\n // More complete entries get higher scores\n let relevanceScore = 0.5;\n if (doc.author_name?.length) relevanceScore += 0.1;\n if (doc.isbn?.length) relevanceScore += 0.15;\n if (doc.cover_i) relevanceScore += 0.1;\n if (doc.first_publish_year) relevanceScore += 0.05;\n if (doc.subject?.length) relevanceScore += 0.05;\n if (doc.ratings_count && doc.ratings_count > 0) relevanceScore += 0.05;\n\n return {\n externalId: doc.key, // Work key, e.g., \"/works/OL45883W\"\n title: doc.title,\n alternateTitles: doc.subtitle ? [doc.subtitle] : [],\n year,\n coverUrl,\n relevanceScore: Math.min(1.0, relevanceScore),\n preview: {\n genres: doc.subject?.slice(0, 5) || [],\n rating: doc.ratings_average\n ? Math.round(doc.ratings_average * 2) / 2 // Normalize to 0-10 scale (OL uses 1-5)\n : undefined,\n authors: doc.author_name?.slice(0, 3) || [],\n description: doc.publisher?.length ? `Published by ${doc.publisher[0]}` : undefined,\n },\n };\n}\n\n/**\n * Resolve author references to full author data\n */\nasync function resolveAuthors(\n authorRefs: OLAuthorReference[] | undefined,\n): Promise<ParsedAuthor[]> {\n if (!authorRefs?.length) return [];\n\n const authors: ParsedAuthor[] = [];\n\n for (const ref of authorRefs) {\n const key = ref.author?.key || ref.key;\n if (!key) continue;\n\n const authorData = await getAuthor(key);\n if (authorData) {\n authors.push({\n name: authorData.name,\n key,\n sortName: authorData.personal_name || undefined,\n });\n }\n }\n\n return authors;\n}\n\n/**\n * Map parsed authors to BookAuthor format\n */\nfunction mapToBookAuthors(authors: ParsedAuthor[]): BookAuthor[] {\n return authors.map((author) => ({\n name: author.name,\n role: \"author\" as const,\n sortName: author.sortName,\n }));\n}\n\n/**\n * Build cover URLs for a book\n */\nfunction buildCoverUrls(isbn: string | undefined, coverId: number | undefined): BookCover[] {\n const covers: BookCover[] = [];\n\n // Prefer ISBN-based URLs as they're more reliable\n if (isbn) {\n covers.push({\n url: getCoverUrlByIsbn(isbn, \"S\"),\n size: \"small\",\n });\n covers.push({\n url: getCoverUrlByIsbn(isbn, \"M\"),\n size: \"medium\",\n });\n covers.push({\n url: getCoverUrlByIsbn(isbn, \"L\"),\n size: \"large\",\n });\n } else if (coverId) {\n // Fallback to cover ID\n covers.push({\n url: getCoverUrlById(coverId, \"S\"),\n size: \"small\",\n });\n covers.push({\n url: getCoverUrlById(coverId, \"M\"),\n size: \"medium\",\n });\n covers.push({\n url: getCoverUrlById(coverId, \"L\"),\n size: \"large\",\n });\n }\n\n return covers;\n}\n\n/**\n * Build external links for Open Library book\n */\nfunction buildExternalLinks(editionKey: string, workKey: string | undefined): ExternalLink[] {\n const links: ExternalLink[] = [\n {\n url: buildOpenLibraryUrl(editionKey),\n label: \"Open Library (Edition)\",\n linkType: \"provider\",\n },\n ];\n\n if (workKey) {\n links.push({\n url: buildOpenLibraryUrl(workKey),\n label: \"Open Library (Work)\",\n linkType: \"provider\",\n });\n }\n\n return links;\n}\n\n/**\n * Get all ISBNs from edition data\n */\nfunction collectIsbns(edition: OLEdition): string[] {\n const isbns: string[] = [];\n\n // Prefer ISBN-13\n if (edition.isbn_13?.length) {\n isbns.push(...edition.isbn_13);\n }\n\n // Add ISBN-10 as well\n if (edition.isbn_10?.length) {\n isbns.push(...edition.isbn_10);\n }\n\n return [...new Set(isbns)]; // Deduplicate\n}\n\n/**\n * Map Open Library edition and optional work to full book metadata\n */\nexport async function mapEditionToBookMetadata(\n edition: OLEdition,\n workData?: OLWork | null,\n): Promise<PluginBookMetadata> {\n // Resolve authors from edition or work\n const authorRefs = edition.authors || workData?.authors;\n const authors = await resolveAuthors(authorRefs);\n\n // Get ISBNs\n const isbns = collectIsbns(edition);\n const primaryIsbn = isbns[0];\n\n // Get cover ID from edition or work\n const coverId = edition.covers?.[0] || workData?.covers?.[0];\n\n // Get description from edition or work\n const description =\n parseDescription(edition.description) || parseDescription(workData?.description);\n\n // Get subjects from both edition and work\n const subjects = [...(edition.subjects || []), ...(workData?.subjects || [])];\n const uniqueSubjects = [...new Set(subjects)];\n\n // Parse year\n const year = parseYear(edition.publish_date);\n const originalYear = parseYear(workData?.first_publish_date);\n\n // Parse language\n const language = parseLanguage(edition.languages?.[0]?.key);\n\n // Build external rating if ratings exist from search\n const externalRatings: ExternalRating[] = [];\n\n // Build metadata\n const workKey = edition.works?.[0]?.key || workData?.key;\n const externalId = workKey || edition.key;\n\n return {\n externalId,\n externalUrl: buildOpenLibraryUrl(externalId),\n\n // Core fields\n title: edition.title,\n subtitle: edition.subtitle || workData?.subtitle,\n alternateTitles: [],\n summary: description,\n bookType: detectBookType(edition),\n\n // Book-specific fields\n pageCount: edition.number_of_pages,\n year,\n\n // ISBN\n isbn: primaryIsbn,\n isbns,\n\n // Edition info\n edition: edition.edition_name,\n originalTitle: workData?.title !== edition.title ? workData?.title : undefined,\n originalYear,\n language,\n\n // Taxonomy\n genres: [], // Open Library doesn't have genres, just subjects\n tags: [],\n subjects: uniqueSubjects.slice(0, 20), // Limit to 20 subjects\n\n // Credits\n authors: mapToBookAuthors(authors),\n artists: [], // Open Library doesn't track artists separately\n publisher: edition.publishers?.[0],\n\n // Media\n coverUrl: primaryIsbn\n ? getCoverUrlByIsbn(primaryIsbn, \"L\")\n : coverId\n ? getCoverUrlById(coverId, \"L\")\n : undefined,\n covers: buildCoverUrls(primaryIsbn, coverId),\n\n // Rating\n externalRatings,\n awards: [],\n\n // Links\n externalLinks: buildExternalLinks(edition.key, workKey),\n };\n}\n\n/**\n * Detect book type from edition data\n *\n * Open Library doesn't have explicit book type, but we can infer from:\n * - physical_format field\n * - subjects\n * - other metadata\n */\nfunction detectBookType(edition: OLEdition): string | undefined {\n const format = edition.physical_format?.toLowerCase();\n\n if (format) {\n if (format.includes(\"comic\") || format.includes(\"graphic novel\")) {\n return \"graphic_novel\";\n }\n if (format.includes(\"manga\")) {\n return \"manga\";\n }\n if (format.includes(\"magazine\") || format.includes(\"periodical\")) {\n return \"magazine\";\n }\n }\n\n // Check subjects for hints\n const subjects = (edition.subjects || []).join(\" \").toLowerCase();\n\n if (subjects.includes(\"graphic novel\") || subjects.includes(\"comics\")) {\n return \"graphic_novel\";\n }\n if (subjects.includes(\"manga\")) {\n return \"manga\";\n }\n\n // Default to novel for most books\n return \"novel\";\n}\n\n/**\n * Map Open Library search doc to book metadata for quick preview\n *\n * This is a lighter version that doesn't fetch additional data\n */\nexport function mapSearchDocToBookPreview(doc: OLSearchDoc): PluginBookMetadata {\n const isbns = doc.isbn?.slice(0, 5) || [];\n const primaryIsbn = isbns[0];\n const coverId = doc.cover_i;\n\n return {\n externalId: doc.key,\n externalUrl: buildOpenLibraryUrl(doc.key),\n\n // Core fields\n title: doc.title,\n subtitle: doc.subtitle,\n alternateTitles: [],\n summary: undefined, // Not available in search results\n\n // Book-specific fields\n pageCount: doc.number_of_pages_median,\n year: doc.first_publish_year,\n\n // ISBN\n isbn: primaryIsbn,\n isbns,\n\n // Taxonomy\n genres: [],\n tags: [],\n subjects: doc.subject?.slice(0, 10) || [],\n\n // Credits\n authors:\n doc.author_name?.map((name) => ({\n name,\n role: \"author\" as const,\n })) || [],\n artists: [],\n publisher: doc.publisher?.[0],\n\n // Media\n coverUrl: primaryIsbn\n ? getCoverUrlByIsbn(primaryIsbn, \"L\")\n : coverId\n ? getCoverUrlById(coverId, \"L\")\n : undefined,\n covers: buildCoverUrls(primaryIsbn, coverId),\n\n // Rating\n rating: doc.ratings_average\n ? {\n score: Math.round(doc.ratings_average * 20), // Convert 1-5 to 0-100\n voteCount: doc.ratings_count,\n source: \"openlibrary\",\n }\n : undefined,\n externalRatings:\n doc.ratings_average && doc.ratings_count\n ? [\n {\n score: Math.round(doc.ratings_average * 20),\n voteCount: doc.ratings_count,\n source: \"openlibrary\",\n },\n ]\n : [],\n awards: [],\n\n // Links\n externalLinks: [\n {\n url: buildOpenLibraryUrl(doc.key),\n label: \"Open Library\",\n linkType: \"provider\",\n },\n ],\n };\n}\n\n/**\n * Get full book metadata by fetching edition, work, and author data\n *\n * @param editionOrWorkKey Either an edition key or work key\n * @param isbn Optional ISBN for direct lookup\n */\nexport async function getFullBookMetadata(\n editionOrWorkKey: string,\n isbn?: string,\n): Promise<PluginBookMetadata | null> {\n // If we have an ISBN, try to get edition directly\n if (isbn) {\n const { getEditionByIsbn } = await import(\"./api.js\");\n const edition = await getEditionByIsbn(isbn);\n if (edition) {\n const workKey = edition.works?.[0]?.key;\n const workData = workKey ? await getWork(workKey) : null;\n return mapEditionToBookMetadata(edition, workData);\n }\n }\n\n // Check if it's a work key\n if (editionOrWorkKey.includes(\"/works/\")) {\n const workData = await getWork(editionOrWorkKey);\n if (!workData) return null;\n\n // Fetch editions directly from the work using the editions API.\n // This is much more reliable than searching by title, which can\n // return completely unrelated books with similar titles.\n const editions = await getWorkEditions(editionOrWorkKey, 5);\n\n if (editions.length > 0) {\n // Prefer an edition that has ISBNs for richer metadata\n const editionWithIsbn = editions.find((e) => e.isbn_13?.length || e.isbn_10?.length);\n const edition = editionWithIsbn || editions[0];\n return mapEditionToBookMetadata(edition, workData);\n }\n\n // Fallback: create metadata from work data only\n const authors = await resolveAuthors(workData.authors);\n const coverId = workData.covers?.[0];\n\n return {\n externalId: workData.key,\n externalUrl: buildOpenLibraryUrl(workData.key),\n title: workData.title,\n subtitle: workData.subtitle,\n alternateTitles: [],\n summary: parseDescription(workData.description),\n isbns: [],\n genres: [],\n tags: [],\n subjects: workData.subjects?.slice(0, 20) || [],\n authors: mapToBookAuthors(authors),\n artists: [],\n coverUrl: coverId ? getCoverUrlById(coverId, \"L\") : undefined,\n covers: coverId\n ? [\n { url: getCoverUrlById(coverId, \"S\"), size: \"small\" },\n { url: getCoverUrlById(coverId, \"M\"), size: \"medium\" },\n { url: getCoverUrlById(coverId, \"L\"), size: \"large\" },\n ]\n : [],\n externalRatings: [],\n awards: [],\n externalLinks: [\n {\n url: buildOpenLibraryUrl(workData.key),\n label: \"Open Library\",\n linkType: \"provider\",\n },\n ],\n };\n }\n\n // It's an edition key - fetch directly\n // For edition keys, we need to use a different approach\n // since there's no direct edition endpoint by key\n // Try to use the key directly\n const url = `https://openlibrary.org${editionOrWorkKey}.json`;\n try {\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": \"Codex/1.0 (https://github.com/AshDevFr/codex; codex-plugin)\",\n Accept: \"application/json\",\n },\n });\n\n if (response.ok) {\n const edition = (await response.json()) as OLEdition;\n const workKey = edition.works?.[0]?.key;\n const workData = workKey ? await getWork(workKey) : null;\n return mapEditionToBookMetadata(edition, workData);\n }\n } catch {\n // Ignore fetch errors\n }\n\n return null;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["params", "url", "manifest", "
|
|
3
|
+
"sources": ["../src/api.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/types/rpc.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/errors.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/logger.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/server.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/storage.ts", "../src/index.ts", "../package.json", "../src/manifest.ts", "../src/mapper.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Open Library API Client\n *\n * Handles communication with the Open Library API with:\n * - Rate limiting (100 requests per 5 minutes recommended)\n * - Caching to reduce API calls\n * - Error handling with retries\n *\n * @see https://openlibrary.org/developers/api\n */\n\nimport type {\n OLAuthor,\n OLEdition,\n OLSearchResponse,\n OLWork,\n OLWorkEditionsResponse,\n} from \"./types.js\";\n\nconst BASE_URL = \"https://openlibrary.org\";\nconst COVERS_BASE_URL = \"https://covers.openlibrary.org\";\n\n// Simple in-memory cache with TTL\ninterface CacheEntry<T> {\n data: T;\n timestamp: number;\n}\n\nconst CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes\nconst cache = new Map<string, CacheEntry<unknown>>();\n\n/**\n * Get cached value if not expired\n */\nfunction getCached<T>(key: string): T | null {\n const entry = cache.get(key);\n if (entry && Date.now() - entry.timestamp < CACHE_TTL_MS) {\n return entry.data as T;\n }\n if (entry) {\n cache.delete(key); // Cleanup expired\n }\n return null;\n}\n\n/**\n * Store value in cache\n */\nfunction setCache<T>(key: string, data: T): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n/**\n * Make an HTTP request with error handling\n */\nasync function fetchJson<T>(url: string, description: string): Promise<T | null> {\n // Check cache first\n const cached = getCached<T>(url);\n if (cached !== null) {\n return cached;\n }\n\n try {\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": \"Codex/1.0 (https://github.com/AshDevFr/codex; codex-plugin)\",\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data = (await response.json()) as T;\n setCache(url, data);\n return data;\n } catch (error) {\n console.error(`[openlibrary] Failed to fetch ${description}:`, error);\n return null;\n }\n}\n\n/**\n * Normalize ISBN by removing hyphens and spaces\n */\nexport function normalizeIsbn(isbn: string): string {\n return isbn.replace(/[-\\s]/g, \"\").toUpperCase();\n}\n\n/**\n * Check if a string is a valid ISBN-10 or ISBN-13\n */\nexport function isValidIsbn(isbn: string): boolean {\n const normalized = normalizeIsbn(isbn);\n return normalized.length === 10 || normalized.length === 13;\n}\n\n/**\n * Fetch book edition by ISBN\n *\n * @param isbn ISBN-10 or ISBN-13\n * @returns Edition data or null if not found\n */\nexport async function getEditionByIsbn(isbn: string): Promise<OLEdition | null> {\n const normalized = normalizeIsbn(isbn);\n const url = `${BASE_URL}/isbn/${normalized}.json`;\n return fetchJson<OLEdition>(url, `edition by ISBN ${normalized}`);\n}\n\n/**\n * Fetch work details by key\n *\n * @param workKey Work key (e.g., \"/works/OL45883W\" or just \"OL45883W\")\n * @returns Work data or null if not found\n */\nexport async function getWork(workKey: string): Promise<OLWork | null> {\n // Normalize key to just the ID part\n const key = workKey.startsWith(\"/works/\") ? workKey : `/works/${workKey}`;\n const url = `${BASE_URL}${key}.json`;\n return fetchJson<OLWork>(url, `work ${key}`);\n}\n\n/**\n * Fetch editions for a work\n *\n * Returns editions directly associated with a work, ordered by most recent.\n * This is more reliable than searching by title, which can return unrelated books.\n *\n * @param workKey Work key (e.g., \"/works/OL45883W\")\n * @param limit Maximum number of editions to fetch\n * @returns Array of editions or empty array if none found\n */\nexport async function getWorkEditions(workKey: string, limit = 5): Promise<OLEdition[]> {\n const key = workKey.startsWith(\"/works/\") ? workKey : `/works/${workKey}`;\n const url = `${BASE_URL}${key}/editions.json?limit=${limit}`;\n const response = await fetchJson<OLWorkEditionsResponse>(url, `editions for ${key}`);\n return response?.entries || [];\n}\n\n/**\n * Fetch author details by key\n *\n * @param authorKey Author key (e.g., \"/authors/OL34184A\" or just \"OL34184A\")\n * @returns Author data or null if not found\n */\nexport async function getAuthor(authorKey: string): Promise<OLAuthor | null> {\n // Normalize key to just the ID part\n const key = authorKey.startsWith(\"/authors/\") ? authorKey : `/authors/${authorKey}`;\n const url = `${BASE_URL}${key}.json`;\n return fetchJson<OLAuthor>(url, `author ${key}`);\n}\n\n/** Fields to request from the Open Library search API */\nconst SEARCH_FIELDS = [\n \"key\",\n \"title\",\n \"subtitle\",\n \"author_name\",\n \"author_key\",\n \"first_publish_year\",\n \"publish_year\",\n \"publisher\",\n \"isbn\",\n \"number_of_pages_median\",\n \"cover_i\",\n \"cover_edition_key\",\n \"edition_count\",\n \"language\",\n \"subject\",\n \"ratings_average\",\n \"ratings_count\",\n].join(\",\");\n\n/**\n * Search for books\n *\n * When an author is provided, uses the `title` + `author` parameters for\n * more precise results. If that yields no results, falls back to a general\n * `q` search to ensure we still return something useful.\n *\n * @param query Search query (title, author, or combined)\n * @param options Additional search options\n * @returns Search results\n */\nexport async function searchBooks(\n query: string,\n options: {\n author?: string;\n limit?: number;\n } = {},\n): Promise<OLSearchResponse | null> {\n const { author, limit = 10 } = options;\n\n // When author is provided, try a refined title + author search first\n if (author) {\n const params = new URLSearchParams({\n title: query,\n author,\n fields: SEARCH_FIELDS,\n limit: String(limit),\n });\n\n const url = `${BASE_URL}/search.json?${params}`;\n const response = await fetchJson<OLSearchResponse>(\n url,\n `search title=\"${query}\" author=\"${author}\"`,\n );\n\n if (response?.docs?.length) {\n return response;\n }\n\n // Fall back to general q search if title+author yielded no results\n }\n\n // General search using q parameter\n const params = new URLSearchParams({\n q: query,\n fields: SEARCH_FIELDS,\n limit: String(limit),\n });\n\n if (author) {\n params.set(\"author\", author);\n }\n\n const url = `${BASE_URL}/search.json?${params}`;\n return fetchJson<OLSearchResponse>(url, `search \"${query}\"`);\n}\n\n/**\n * Get cover image URL by ISBN\n *\n * @param isbn ISBN-10 or ISBN-13\n * @param size Cover size: S (small ~50w), M (medium ~180w), L (large ~300w+)\n * @returns Cover URL\n */\nexport function getCoverUrlByIsbn(isbn: string, size: \"S\" | \"M\" | \"L\"): string {\n const normalized = normalizeIsbn(isbn);\n return `${COVERS_BASE_URL}/b/isbn/${normalized}-${size}.jpg`;\n}\n\n/**\n * Get cover image URL by cover ID\n *\n * @param coverId Open Library cover ID\n * @param size Cover size: S (small), M (medium), L (large)\n * @returns Cover URL\n */\nexport function getCoverUrlById(coverId: number, size: \"S\" | \"M\" | \"L\"): string {\n return `${COVERS_BASE_URL}/b/id/${coverId}-${size}.jpg`;\n}\n\n/**\n * Get cover image URL by Open Library ID (OLID)\n *\n * @param olid Open Library ID (e.g., \"OL7353617M\" for edition, \"OL45883W\" for work)\n * @param size Cover size: S (small), M (medium), L (large)\n * @returns Cover URL\n */\nexport function getCoverUrlByOlid(olid: string, size: \"S\" | \"M\" | \"L\"): string {\n // Strip any prefix if present\n const id = olid.replace(/^\\/(?:books|works)\\//, \"\");\n return `${COVERS_BASE_URL}/b/olid/${id}-${size}.jpg`;\n}\n\n/**\n * Parse year from Open Library date string\n *\n * Open Library dates can be in various formats:\n * - \"2020\"\n * - \"January 1, 2020\"\n * - \"2020-01-15\"\n * - \"c1985\"\n * - \"1985?\"\n *\n * @param dateStr Date string from Open Library\n * @returns Parsed year or undefined if unable to parse\n */\nexport function parseYear(dateStr: string | undefined): number | undefined {\n if (!dateStr) return undefined;\n\n // Try to extract a 4-digit year\n // Using (?:^|[^0-9]) to handle \"c1985\" format where there's no word boundary\n const match = dateStr.match(/(?:^|[^0-9])(1[89]\\d{2}|20\\d{2})(?:[^0-9]|$)/);\n if (match) {\n return Number.parseInt(match[1], 10);\n }\n\n return undefined;\n}\n\n/**\n * Parse description from Open Library\n *\n * Description can be either a string or an object with { type, value }.\n * Strips HTML tags and normalizes whitespace, since Open Library descriptions\n * can contain raw HTML (e.g., from Standard Ebooks imports).\n */\nexport function parseDescription(\n desc: string | { type?: string; value: string } | undefined,\n): string | undefined {\n if (!desc) return undefined;\n const raw = typeof desc === \"string\" ? desc : desc.value;\n return stripHtml(raw);\n}\n\n/**\n * Strip HTML tags from a string and normalize whitespace.\n *\n * Converts block-level tags (p, br, div, li) to newlines,\n * strips all remaining tags, decodes common HTML entities,\n * and collapses excessive whitespace.\n */\nfunction stripHtml(html: string): string | undefined {\n let text = html;\n\n // Convert block-level elements to newlines\n text = text.replace(/<\\/(p|div|li|tr|h[1-6])>/gi, \"\\n\");\n text = text.replace(/<br\\s*\\/?>/gi, \"\\n\");\n\n // Remove all remaining HTML tags\n text = text.replace(/<[^>]+>/g, \"\");\n\n // Decode common HTML entities\n text = text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n .replace(/ /g, \" \");\n\n // Collapse multiple spaces/tabs on the same line into one space\n text = text.replace(/[^\\S\\n]+/g, \" \");\n\n // Collapse 3+ consecutive newlines into 2\n text = text.replace(/\\n{3,}/g, \"\\n\\n\");\n\n // Trim each line and remove leading/trailing whitespace\n text = text\n .split(\"\\n\")\n .map((line) => line.trim())\n .join(\"\\n\")\n .trim();\n\n return text || undefined;\n}\n\n/**\n * Convert Open Library language code to BCP47\n *\n * Open Library uses format like \"/languages/eng\"\n *\n * @param langRef Language reference (e.g., \"/languages/eng\")\n * @returns BCP47 language code (e.g., \"en\")\n */\nexport function parseLanguage(langRef: string | undefined): string | undefined {\n if (!langRef) return undefined;\n\n // Extract language code from \"/languages/xxx\" format\n const match = langRef.match(/\\/languages\\/(\\w+)$/);\n if (!match) return undefined;\n\n const code = match[1].toLowerCase();\n\n // Map Open Library 3-letter codes to BCP47 2-letter codes\n const languageMap: Record<string, string> = {\n eng: \"en\",\n spa: \"es\",\n fre: \"fr\",\n fra: \"fr\",\n ger: \"de\",\n deu: \"de\",\n ita: \"it\",\n por: \"pt\",\n rus: \"ru\",\n jpn: \"ja\",\n chi: \"zh\",\n zho: \"zh\",\n kor: \"ko\",\n ara: \"ar\",\n hin: \"hi\",\n pol: \"pl\",\n tur: \"tr\",\n dut: \"nl\",\n nld: \"nl\",\n swe: \"sv\",\n nor: \"no\",\n dan: \"da\",\n fin: \"fi\",\n cze: \"cs\",\n ces: \"cs\",\n gre: \"el\",\n ell: \"el\",\n heb: \"he\",\n hun: \"hu\",\n rom: \"ro\",\n ron: \"ro\",\n tha: \"th\",\n vie: \"vi\",\n ind: \"id\",\n mal: \"ms\",\n msa: \"ms\",\n ukr: \"uk\",\n cat: \"ca\",\n lat: \"la\",\n };\n\n return languageMap[code] || code;\n}\n\n/**\n * Extract Open Library ID from a key\n *\n * @param key Full key (e.g., \"/works/OL45883W\" or \"/books/OL7353617M\")\n * @returns Just the ID (e.g., \"OL45883W\" or \"OL7353617M\")\n */\nexport function extractOlid(key: string): string {\n return key.replace(/^\\/(?:works|books|authors)\\//, \"\");\n}\n\n/**\n * Build Open Library URL from a key\n *\n * @param key Key (e.g., \"/works/OL45883W\")\n * @returns Full URL (e.g., \"https://openlibrary.org/works/OL45883W\")\n */\nexport function buildOpenLibraryUrl(key: string): string {\n return `${BASE_URL}${key.startsWith(\"/\") ? key : `/${key}`}`;\n}\n\n/**\n * Clear the cache\n */\nexport function clearCache(): void {\n cache.clear();\n}\n", null, null, null, null, null, "/**\n * Open Library Metadata Plugin for Codex\n *\n * Fetches book metadata from Open Library (openlibrary.org), a free and open\n * book database with extensive ISBN coverage.\n *\n * Features:\n * - ISBN lookup for direct, accurate matching\n * - Title/author search for fuzzy matching\n * - Cover image fetching in multiple sizes\n * - Author resolution with proper names\n * - Subject/genre extraction\n *\n * @see https://openlibrary.org/developers/api\n */\n\nimport {\n type BookMatchParams,\n type BookMetadataProvider,\n type BookSearchParams,\n createLogger,\n createMetadataPlugin,\n type InitializeParams,\n type MetadataGetParams,\n type MetadataMatchResponse,\n type MetadataSearchResponse,\n type PluginBookMetadata,\n} from \"@ashdev/codex-plugin-sdk\";\n\nimport { getEditionByIsbn, getWork, isValidIsbn, searchBooks } from \"./api.js\";\nimport { DEFAULT_MAX_RESULTS, manifest } from \"./manifest.js\";\nimport {\n getFullBookMetadata,\n mapEditionToBookMetadata,\n mapSearchDocToSearchResult,\n} from \"./mapper.js\";\n\nconst logger = createLogger({ name: \"openlibrary\", level: \"info\" });\n\n// Plugin configuration (set during initialization)\nconst config = {\n maxResults: DEFAULT_MAX_RESULTS,\n};\n\n/**\n * Book metadata provider implementation\n */\nconst bookProvider: BookMetadataProvider = {\n /**\n * Search for books by ISBN or title/author query\n *\n * If ISBN is provided, it takes priority for direct lookup.\n * Otherwise, falls back to title/author search.\n */\n async search(params: BookSearchParams): Promise<MetadataSearchResponse> {\n const { isbn, query, author, limit } = params;\n const maxResults = Math.min(limit || config.maxResults, 50);\n\n // If ISBN is provided, try direct lookup first\n if (isbn && isValidIsbn(isbn)) {\n const edition = await getEditionByIsbn(isbn);\n\n if (edition) {\n // Found by ISBN - return as single result with high relevance\n const workKey = edition.works?.[0]?.key;\n const workData = workKey ? await getWork(workKey) : null;\n const metadata = await mapEditionToBookMetadata(edition, workData);\n\n return {\n results: [\n {\n externalId: metadata.externalId,\n title: metadata.title || \"Unknown\",\n alternateTitles: metadata.subtitle ? [metadata.subtitle] : [],\n year: metadata.year,\n coverUrl: metadata.coverUrl,\n relevanceScore: 1.0, // Perfect match by ISBN\n preview: {\n genres: metadata.subjects.slice(0, 5),\n authors: metadata.authors.map((a) => a.name),\n },\n },\n ],\n };\n }\n\n // ISBN not found, fall through to search if query is also provided\n if (!query) {\n return { results: [] };\n }\n }\n\n // Title/author search\n if (!query) {\n return { results: [] };\n }\n\n const searchResponse = await searchBooks(query, {\n author,\n limit: maxResults,\n });\n\n if (!searchResponse?.docs?.length) {\n return { results: [] };\n }\n\n return {\n results: searchResponse.docs.map(mapSearchDocToSearchResult),\n };\n },\n\n /**\n * Get full book metadata by external ID\n *\n * The external ID can be:\n * - A work key: \"/works/OL45883W\"\n * - An edition key: \"/books/OL7353617M\"\n */\n async get(params: MetadataGetParams): Promise<PluginBookMetadata> {\n const { externalId } = params;\n\n // Try to get full metadata\n const metadata = await getFullBookMetadata(externalId);\n\n if (metadata) {\n return metadata;\n }\n\n // Fallback: return minimal metadata\n return {\n externalId,\n externalUrl: `https://openlibrary.org${externalId.startsWith(\"/\") ? externalId : `/${externalId}`}`,\n alternateTitles: [],\n isbns: [],\n genres: [],\n tags: [],\n subjects: [],\n authors: [],\n artists: [],\n covers: [],\n externalRatings: [],\n awards: [],\n externalLinks: [\n {\n url: `https://openlibrary.org${externalId.startsWith(\"/\") ? externalId : `/${externalId}`}`,\n label: \"Open Library\",\n linkType: \"provider\",\n },\n ],\n };\n },\n\n /**\n * Auto-match a book using available identifiers\n *\n * Match priority:\n * 1. ISBN (if provided) - highest confidence\n * 2. Title + author search - lower confidence\n */\n async match(params: BookMatchParams): Promise<MetadataMatchResponse> {\n const { title, authors, isbn, year } = params;\n\n // Try ISBN first if available\n if (isbn && isValidIsbn(isbn)) {\n const edition = await getEditionByIsbn(isbn);\n\n if (edition) {\n const workKey = edition.works?.[0]?.key;\n const workData = workKey ? await getWork(workKey) : null;\n const metadata = await mapEditionToBookMetadata(edition, workData);\n\n return {\n match: {\n externalId: metadata.externalId,\n title: metadata.title || \"Unknown\",\n alternateTitles: metadata.subtitle ? [metadata.subtitle] : [],\n year: metadata.year,\n coverUrl: metadata.coverUrl,\n relevanceScore: 1.0,\n preview: {\n genres: metadata.subjects.slice(0, 5),\n authors: metadata.authors.map((a) => a.name),\n },\n },\n confidence: 0.99, // Very high confidence for ISBN match\n alternatives: [],\n };\n }\n }\n\n // Fall back to title search\n const searchQuery = authors?.length ? `${title} ${authors[0]}` : title;\n\n const searchResponse = await searchBooks(searchQuery, {\n limit: 5,\n });\n\n if (!searchResponse?.docs?.length) {\n return {\n match: null,\n confidence: 0,\n alternatives: [],\n };\n }\n\n const results = searchResponse.docs.map(mapSearchDocToSearchResult);\n\n // Calculate confidence based on title similarity and other factors\n const bestMatch = results[0];\n let confidence = bestMatch.relevanceScore || 0.5;\n\n // Boost confidence if title matches closely\n const normalizedTitle = title.toLowerCase().trim();\n const normalizedMatchTitle = bestMatch.title.toLowerCase().trim();\n\n if (normalizedTitle === normalizedMatchTitle) {\n confidence = Math.min(1.0, confidence + 0.3);\n } else if (\n normalizedMatchTitle.includes(normalizedTitle) ||\n normalizedTitle.includes(normalizedMatchTitle)\n ) {\n confidence = Math.min(1.0, confidence + 0.15);\n }\n\n // Boost if year matches\n if (year && bestMatch.year === year) {\n confidence = Math.min(1.0, confidence + 0.1);\n }\n\n // Reduce confidence without ISBN\n confidence = Math.min(confidence, 0.85);\n\n return {\n match: bestMatch,\n confidence,\n alternatives: results.slice(1),\n };\n },\n};\n\n// =============================================================================\n// Plugin Initialization\n// =============================================================================\n\ncreateMetadataPlugin({\n manifest,\n bookProvider,\n logLevel: \"info\",\n onInitialize(params: InitializeParams) {\n // Read config from initialization params\n const maxResults = params.adminConfig?.maxResults as number | undefined;\n if (maxResults !== undefined) {\n config.maxResults = Math.min(Math.max(1, maxResults), 50); // Clamp 1-50\n }\n logger.info(`Plugin initialized (maxResults: ${config.maxResults})`);\n },\n});\n\nlogger.info(\"Open Library plugin started\");\n", "{\n \"name\": \"@ashdev/codex-plugin-metadata-openlibrary\",\n \"version\": \"1.10.0\",\n \"description\": \"Open Library metadata plugin for Codex - fetches book metadata by ISBN or title search\",\n \"main\": \"dist/index.js\",\n \"bin\": \"dist/index.js\",\n \"type\": \"module\",\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AshDevFr/codex.git\",\n \"directory\": \"plugins/metadata-openlibrary\"\n },\n \"scripts\": {\n \"build\": \"esbuild src/index.ts --bundle --platform=node --target=node22 --format=esm --outfile=dist/index.js --sourcemap --banner:js='#!/usr/bin/env node'\",\n \"dev\": \"npm run build -- --watch\",\n \"clean\": \"rm -rf dist\",\n \"start\": \"node dist/index.js\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"npm run lint && npm run build\"\n },\n \"keywords\": [\n \"codex\",\n \"plugin\",\n \"openlibrary\",\n \"metadata\",\n \"books\",\n \"isbn\"\n ],\n \"author\": \"Codex\",\n \"license\": \"MIT\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"dependencies\": {\n \"@ashdev/codex-plugin-sdk\": \"^1.10.0\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.3.13\",\n \"@types/node\": \"^22.0.0\",\n \"esbuild\": \"^0.24.0\",\n \"typescript\": \"^5.7.0\",\n \"vitest\": \"^3.0.0\"\n }\n}\n", "import type { MetadataContentType, PluginManifest } from \"@ashdev/codex-plugin-sdk\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\n// Default config values\nexport const DEFAULT_MAX_RESULTS = 10;\n\nexport const manifest = {\n name: \"metadata-openlibrary\",\n displayName: \"Open Library\",\n version: packageJson.version,\n description:\n \"Fetches book metadata from Open Library (openlibrary.org). Supports ISBN lookup and title search for EPUBs, PDFs, and other book formats.\",\n author: \"Codex\",\n homepage: \"https://openlibrary.org\",\n protocolVersion: \"1.0\",\n capabilities: {\n // Book metadata provider only (not series)\n metadataProvider: [\"book\"] as MetadataContentType[],\n },\n configSchema: {\n description: \"Configuration options for the Open Library plugin\",\n fields: [\n {\n key: \"maxResults\",\n label: \"Maximum Results\",\n description: \"Maximum number of results to return for search queries (1-50)\",\n type: \"number\" as const,\n required: false,\n default: DEFAULT_MAX_RESULTS,\n example: 20,\n },\n ],\n },\n} as const satisfies PluginManifest & {\n capabilities: { metadataProvider: MetadataContentType[] };\n};\n", "/**\n * Mapper functions to convert Open Library data to Codex plugin format\n */\n\nimport type {\n BookAuthor,\n BookCover,\n ExternalLink,\n ExternalRating,\n PluginBookMetadata,\n SearchResult,\n} from \"@ashdev/codex-plugin-sdk\";\n\nimport {\n buildOpenLibraryUrl,\n getAuthor,\n getCoverUrlById,\n getCoverUrlByIsbn,\n getWork,\n getWorkEditions,\n parseDescription,\n parseLanguage,\n parseYear,\n} from \"./api.js\";\nimport type { OLAuthorReference, OLEdition, OLSearchDoc, OLWork, ParsedAuthor } from \"./types.js\";\n\n/**\n * Map Open Library search result to Codex SearchResult\n */\nexport function mapSearchDocToSearchResult(doc: OLSearchDoc): SearchResult {\n const year = doc.first_publish_year;\n const coverUrl = doc.cover_i ? getCoverUrlById(doc.cover_i, \"M\") : undefined;\n\n // Calculate a relevance score based on available data\n // More complete entries get higher scores\n let relevanceScore = 0.5;\n if (doc.author_name?.length) relevanceScore += 0.1;\n if (doc.isbn?.length) relevanceScore += 0.15;\n if (doc.cover_i) relevanceScore += 0.1;\n if (doc.first_publish_year) relevanceScore += 0.05;\n if (doc.subject?.length) relevanceScore += 0.05;\n if (doc.ratings_count && doc.ratings_count > 0) relevanceScore += 0.05;\n\n return {\n externalId: doc.key, // Work key, e.g., \"/works/OL45883W\"\n title: doc.title,\n alternateTitles: doc.subtitle ? [doc.subtitle] : [],\n year,\n coverUrl,\n relevanceScore: Math.min(1.0, relevanceScore),\n preview: {\n genres: doc.subject?.slice(0, 5) || [],\n rating: doc.ratings_average\n ? Math.round(doc.ratings_average * 2) / 2 // Normalize to 0-10 scale (OL uses 1-5)\n : undefined,\n authors: doc.author_name?.slice(0, 3) || [],\n description: doc.publisher?.length ? `Published by ${doc.publisher[0]}` : undefined,\n },\n };\n}\n\n/**\n * Resolve author references to full author data\n */\nasync function resolveAuthors(\n authorRefs: OLAuthorReference[] | undefined,\n): Promise<ParsedAuthor[]> {\n if (!authorRefs?.length) return [];\n\n const authors: ParsedAuthor[] = [];\n\n for (const ref of authorRefs) {\n const key = ref.author?.key || ref.key;\n if (!key) continue;\n\n const authorData = await getAuthor(key);\n if (authorData) {\n authors.push({\n name: authorData.name,\n key,\n sortName: authorData.personal_name || undefined,\n });\n }\n }\n\n return authors;\n}\n\n/**\n * Map parsed authors to BookAuthor format\n */\nfunction mapToBookAuthors(authors: ParsedAuthor[]): BookAuthor[] {\n return authors.map((author) => ({\n name: author.name,\n role: \"author\" as const,\n sortName: author.sortName,\n }));\n}\n\n/**\n * Build cover URLs for a book\n */\nfunction buildCoverUrls(isbn: string | undefined, coverId: number | undefined): BookCover[] {\n const covers: BookCover[] = [];\n\n // Prefer ISBN-based URLs as they're more reliable\n if (isbn) {\n covers.push({\n url: getCoverUrlByIsbn(isbn, \"S\"),\n size: \"small\",\n });\n covers.push({\n url: getCoverUrlByIsbn(isbn, \"M\"),\n size: \"medium\",\n });\n covers.push({\n url: getCoverUrlByIsbn(isbn, \"L\"),\n size: \"large\",\n });\n } else if (coverId) {\n // Fallback to cover ID\n covers.push({\n url: getCoverUrlById(coverId, \"S\"),\n size: \"small\",\n });\n covers.push({\n url: getCoverUrlById(coverId, \"M\"),\n size: \"medium\",\n });\n covers.push({\n url: getCoverUrlById(coverId, \"L\"),\n size: \"large\",\n });\n }\n\n return covers;\n}\n\n/**\n * Build external links for Open Library book\n */\nfunction buildExternalLinks(editionKey: string, workKey: string | undefined): ExternalLink[] {\n const links: ExternalLink[] = [\n {\n url: buildOpenLibraryUrl(editionKey),\n label: \"Open Library (Edition)\",\n linkType: \"provider\",\n },\n ];\n\n if (workKey) {\n links.push({\n url: buildOpenLibraryUrl(workKey),\n label: \"Open Library (Work)\",\n linkType: \"provider\",\n });\n }\n\n return links;\n}\n\n/**\n * Get all ISBNs from edition data\n */\nfunction collectIsbns(edition: OLEdition): string[] {\n const isbns: string[] = [];\n\n // Prefer ISBN-13\n if (edition.isbn_13?.length) {\n isbns.push(...edition.isbn_13);\n }\n\n // Add ISBN-10 as well\n if (edition.isbn_10?.length) {\n isbns.push(...edition.isbn_10);\n }\n\n return [...new Set(isbns)]; // Deduplicate\n}\n\n/**\n * Map Open Library edition and optional work to full book metadata\n */\nexport async function mapEditionToBookMetadata(\n edition: OLEdition,\n workData?: OLWork | null,\n): Promise<PluginBookMetadata> {\n // Resolve authors from edition or work\n const authorRefs = edition.authors || workData?.authors;\n const authors = await resolveAuthors(authorRefs);\n\n // Get ISBNs\n const isbns = collectIsbns(edition);\n const primaryIsbn = isbns[0];\n\n // Get cover ID from edition or work\n const coverId = edition.covers?.[0] || workData?.covers?.[0];\n\n // Get description from edition or work\n const description =\n parseDescription(edition.description) || parseDescription(workData?.description);\n\n // Get subjects from both edition and work\n const subjects = [...(edition.subjects || []), ...(workData?.subjects || [])];\n const uniqueSubjects = [...new Set(subjects)];\n\n // Parse year\n const year = parseYear(edition.publish_date);\n const originalYear = parseYear(workData?.first_publish_date);\n\n // Parse language\n const language = parseLanguage(edition.languages?.[0]?.key);\n\n // Build external rating if ratings exist from search\n const externalRatings: ExternalRating[] = [];\n\n // Build metadata\n const workKey = edition.works?.[0]?.key || workData?.key;\n const externalId = workKey || edition.key;\n\n return {\n externalId,\n externalUrl: buildOpenLibraryUrl(externalId),\n\n // Core fields\n title: edition.title,\n subtitle: edition.subtitle || workData?.subtitle,\n alternateTitles: [],\n summary: description,\n bookType: detectBookType(edition),\n\n // Book-specific fields\n pageCount: edition.number_of_pages,\n year,\n\n // ISBN\n isbn: primaryIsbn,\n isbns,\n\n // Edition info\n edition: edition.edition_name,\n originalTitle: workData?.title !== edition.title ? workData?.title : undefined,\n originalYear,\n language,\n\n // Taxonomy\n genres: [], // Open Library doesn't have genres, just subjects\n tags: [],\n subjects: uniqueSubjects.slice(0, 20), // Limit to 20 subjects\n\n // Credits\n authors: mapToBookAuthors(authors),\n artists: [], // Open Library doesn't track artists separately\n publisher: edition.publishers?.[0],\n\n // Media\n coverUrl: primaryIsbn\n ? getCoverUrlByIsbn(primaryIsbn, \"L\")\n : coverId\n ? getCoverUrlById(coverId, \"L\")\n : undefined,\n covers: buildCoverUrls(primaryIsbn, coverId),\n\n // Rating\n externalRatings,\n awards: [],\n\n // Links\n externalLinks: buildExternalLinks(edition.key, workKey),\n };\n}\n\n/**\n * Detect book type from edition data\n *\n * Open Library doesn't have explicit book type, but we can infer from:\n * - physical_format field\n * - subjects\n * - other metadata\n */\nfunction detectBookType(edition: OLEdition): string | undefined {\n const format = edition.physical_format?.toLowerCase();\n\n if (format) {\n if (format.includes(\"comic\") || format.includes(\"graphic novel\")) {\n return \"graphic_novel\";\n }\n if (format.includes(\"manga\")) {\n return \"manga\";\n }\n if (format.includes(\"magazine\") || format.includes(\"periodical\")) {\n return \"magazine\";\n }\n }\n\n // Check subjects for hints\n const subjects = (edition.subjects || []).join(\" \").toLowerCase();\n\n if (subjects.includes(\"graphic novel\") || subjects.includes(\"comics\")) {\n return \"graphic_novel\";\n }\n if (subjects.includes(\"manga\")) {\n return \"manga\";\n }\n\n // Default to novel for most books\n return \"novel\";\n}\n\n/**\n * Map Open Library search doc to book metadata for quick preview\n *\n * This is a lighter version that doesn't fetch additional data\n */\nexport function mapSearchDocToBookPreview(doc: OLSearchDoc): PluginBookMetadata {\n const isbns = doc.isbn?.slice(0, 5) || [];\n const primaryIsbn = isbns[0];\n const coverId = doc.cover_i;\n\n return {\n externalId: doc.key,\n externalUrl: buildOpenLibraryUrl(doc.key),\n\n // Core fields\n title: doc.title,\n subtitle: doc.subtitle,\n alternateTitles: [],\n summary: undefined, // Not available in search results\n\n // Book-specific fields\n pageCount: doc.number_of_pages_median,\n year: doc.first_publish_year,\n\n // ISBN\n isbn: primaryIsbn,\n isbns,\n\n // Taxonomy\n genres: [],\n tags: [],\n subjects: doc.subject?.slice(0, 10) || [],\n\n // Credits\n authors:\n doc.author_name?.map((name) => ({\n name,\n role: \"author\" as const,\n })) || [],\n artists: [],\n publisher: doc.publisher?.[0],\n\n // Media\n coverUrl: primaryIsbn\n ? getCoverUrlByIsbn(primaryIsbn, \"L\")\n : coverId\n ? getCoverUrlById(coverId, \"L\")\n : undefined,\n covers: buildCoverUrls(primaryIsbn, coverId),\n\n // Rating\n rating: doc.ratings_average\n ? {\n score: Math.round(doc.ratings_average * 20), // Convert 1-5 to 0-100\n voteCount: doc.ratings_count,\n source: \"openlibrary\",\n }\n : undefined,\n externalRatings:\n doc.ratings_average && doc.ratings_count\n ? [\n {\n score: Math.round(doc.ratings_average * 20),\n voteCount: doc.ratings_count,\n source: \"openlibrary\",\n },\n ]\n : [],\n awards: [],\n\n // Links\n externalLinks: [\n {\n url: buildOpenLibraryUrl(doc.key),\n label: \"Open Library\",\n linkType: \"provider\",\n },\n ],\n };\n}\n\n/**\n * Get full book metadata by fetching edition, work, and author data\n *\n * @param editionOrWorkKey Either an edition key or work key\n * @param isbn Optional ISBN for direct lookup\n */\nexport async function getFullBookMetadata(\n editionOrWorkKey: string,\n isbn?: string,\n): Promise<PluginBookMetadata | null> {\n // If we have an ISBN, try to get edition directly\n if (isbn) {\n const { getEditionByIsbn } = await import(\"./api.js\");\n const edition = await getEditionByIsbn(isbn);\n if (edition) {\n const workKey = edition.works?.[0]?.key;\n const workData = workKey ? await getWork(workKey) : null;\n return mapEditionToBookMetadata(edition, workData);\n }\n }\n\n // Check if it's a work key\n if (editionOrWorkKey.includes(\"/works/\")) {\n const workData = await getWork(editionOrWorkKey);\n if (!workData) return null;\n\n // Fetch editions directly from the work using the editions API.\n // This is much more reliable than searching by title, which can\n // return completely unrelated books with similar titles.\n const editions = await getWorkEditions(editionOrWorkKey, 5);\n\n if (editions.length > 0) {\n // Prefer an edition that has ISBNs for richer metadata\n const editionWithIsbn = editions.find((e) => e.isbn_13?.length || e.isbn_10?.length);\n const edition = editionWithIsbn || editions[0];\n return mapEditionToBookMetadata(edition, workData);\n }\n\n // Fallback: create metadata from work data only\n const authors = await resolveAuthors(workData.authors);\n const coverId = workData.covers?.[0];\n\n return {\n externalId: workData.key,\n externalUrl: buildOpenLibraryUrl(workData.key),\n title: workData.title,\n subtitle: workData.subtitle,\n alternateTitles: [],\n summary: parseDescription(workData.description),\n isbns: [],\n genres: [],\n tags: [],\n subjects: workData.subjects?.slice(0, 20) || [],\n authors: mapToBookAuthors(authors),\n artists: [],\n coverUrl: coverId ? getCoverUrlById(coverId, \"L\") : undefined,\n covers: coverId\n ? [\n { url: getCoverUrlById(coverId, \"S\"), size: \"small\" },\n { url: getCoverUrlById(coverId, \"M\"), size: \"medium\" },\n { url: getCoverUrlById(coverId, \"L\"), size: \"large\" },\n ]\n : [],\n externalRatings: [],\n awards: [],\n externalLinks: [\n {\n url: buildOpenLibraryUrl(workData.key),\n label: \"Open Library\",\n linkType: \"provider\",\n },\n ],\n };\n }\n\n // It's an edition key - fetch directly\n // For edition keys, we need to use a different approach\n // since there's no direct edition endpoint by key\n // Try to use the key directly\n const url = `https://openlibrary.org${editionOrWorkKey}.json`;\n try {\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": \"Codex/1.0 (https://github.com/AshDevFr/codex; codex-plugin)\",\n Accept: \"application/json\",\n },\n });\n\n if (response.ok) {\n const edition = (await response.json()) as OLEdition;\n const workKey = edition.works?.[0]?.key;\n const workData = workKey ? await getWork(workKey) : null;\n return mapEditionToBookMetadata(edition, workData);\n }\n } catch {\n // Ignore fetch errors\n }\n\n return null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["params", "url", "manifest", "logger", "response", "bookProvider", "getEditionByIsbn"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ashdev/codex-plugin-metadata-openlibrary",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Open Library metadata plugin for Codex - fetches book metadata by ISBN or title search",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": "dist/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=22.0.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@ashdev/codex-plugin-sdk": "^1.
|
|
43
|
+
"@ashdev/codex-plugin-sdk": "^1.10.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@biomejs/biome": "^2.3.13",
|