@shisho/plugin-sdk 0.0.26 → 0.0.29

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/host-api.d.ts CHANGED
@@ -395,6 +395,28 @@ export interface ShishoShell {
395
395
  export interface ShishoHostAPI {
396
396
  /** Persistent data directory for this plugin. Created lazily on first access. */
397
397
  readonly dataDir: string;
398
+ /**
399
+ * Block the plugin for the given number of milliseconds.
400
+ *
401
+ * Goja has no Promise/setTimeout support, so this is a synchronous delay
402
+ * backed by Go's `time.Sleep`. Use it to implement exponential backoff
403
+ * between retries when calling rate-limited APIs. The sleep honors the
404
+ * hook's deadline: if the ctx is cancelled while the plugin is sleeping,
405
+ * the call unblocks immediately and the hook throws.
406
+ *
407
+ * @param ms Finite non-negative milliseconds. Throws on negative, NaN, or
408
+ * Infinity.
409
+ * @example
410
+ * // Exponential backoff: 1s, 2s, 4s
411
+ * var resp;
412
+ * for (var attempt = 0; attempt < 3; attempt++) {
413
+ * resp = shisho.http.fetch(url, {});
414
+ * if (resp.status !== 503) return resp;
415
+ * shisho.sleep(1000 * Math.pow(2, attempt));
416
+ * }
417
+ * return resp; // retries exhausted — return the last 503
418
+ */
419
+ sleep(ms: number): void;
398
420
  log: ShishoLog;
399
421
  config: ShishoConfig;
400
422
  http: ShishoHTTP;
package/manifest.d.ts CHANGED
@@ -45,7 +45,9 @@ export type MetadataField =
45
45
  | "url"
46
46
  | "releaseDate"
47
47
  | "cover"
48
- | "identifiers";
48
+ | "identifiers"
49
+ | "language"
50
+ | "abridged";
49
51
 
50
52
  /** Metadata enricher capability declaration. */
51
53
  export interface MetadataEnricherCap {
package/metadata.d.ts CHANGED
@@ -42,6 +42,10 @@ export interface ParsedMetadata {
42
42
  publisher?: string;
43
43
  imprint?: string;
44
44
  url?: string;
45
+ /** BCP 47 language tag (e.g., "en", "en-US", "zh-Hans"). */
46
+ language?: string;
47
+ /** Whether this is an abridged edition. true=abridged, false=unabridged, undefined=unknown. */
48
+ abridged?: boolean;
45
49
  /** ISO 8601 date string (e.g., "2023-06-15T00:00:00Z"). */
46
50
  releaseDate?: string;
47
51
  /** MIME type of cover image (e.g., "image/jpeg"). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shisho/plugin-sdk",
3
- "version": "0.0.26",
3
+ "version": "0.0.29",
4
4
  "description": "TypeScript SDK for Shisho plugin development — types, host API declarations, and test utilities",
5
5
  "homepage": "https://github.com/shishobooks/shisho/blob/master/packages/plugin-sdk/README.md",
6
6
  "types": "index.d.ts",
@@ -26,7 +26,7 @@
26
26
  "directory": "packages/plugin-sdk"
27
27
  },
28
28
  "dependencies": {
29
- "fast-xml-parser": "^5.5.9",
29
+ "fast-xml-parser": "^5.5.11",
30
30
  "linkedom": "^0.18.12"
31
31
  }
32
32
  }
@@ -17,10 +17,21 @@ export interface MockShishoOptions {
17
17
  /**
18
18
  * Path-based filesystem mock.
19
19
  * - string values are returned by readTextFile/readFile
20
- * - Buffer values are returned by readFile (as ArrayBuffer)
20
+ * - Uint8Array (or Buffer) values are returned by readFile (as ArrayBuffer)
21
21
  * - string[] values are returned by listDir
22
22
  */
23
- fs?: Record<string, string | Buffer | string[]>;
23
+ fs?: Record<string, string | Uint8Array | string[]>;
24
+ /**
25
+ * Override the `sleep` implementation. Defaults to a no-op so tests asserting
26
+ * exponential backoff with e.g. `shisho.sleep(1000 * Math.pow(2, attempt))`
27
+ * don't actually block for the cumulative wall-clock time. Wrap with your
28
+ * test framework's spy helper (e.g. `vi.fn()`) if you want to assert the
29
+ * arguments each retry passed.
30
+ *
31
+ * The override is still subject to the same validation rules as production
32
+ * (no NaN/Infinity/negative).
33
+ */
34
+ sleep?: (ms: number) => void;
24
35
  }
25
36
  /**
26
37
  * Create a mock `shisho` host API object for testing plugins.
package/testing/index.js CHANGED
@@ -361,7 +361,6 @@ export function createMockShisho(options = {}) {
361
361
  const encoder = new TextEncoder();
362
362
  return encoder.encode(entry).buffer;
363
363
  }
364
- // Buffer
365
364
  return entry.buffer.slice(entry.byteOffset, entry.byteOffset + entry.byteLength);
366
365
  },
367
366
  readTextFile(path) {
@@ -379,7 +378,6 @@ export function createMockShisho(options = {}) {
379
378
  if (typeof entry === "string") {
380
379
  return entry;
381
380
  }
382
- // Buffer -> string
383
381
  const decoder = new TextDecoder();
384
382
  return decoder.decode(entry);
385
383
  },
@@ -418,8 +416,24 @@ export function createMockShisho(options = {}) {
418
416
  throw new Error(`Mock ${api}: not implemented. Use MockShishoOptions to configure the APIs you need, ` +
419
417
  `or provide your own mock for ${api}.`);
420
418
  };
419
+ // --- sleep: validate like production, then delegate ---
420
+ // Defaults to a no-op because plugin tests asserting exponential backoff
421
+ // (`shisho.sleep(1000 * Math.pow(2, attempt))`) would otherwise add several
422
+ // seconds of wall-clock per retry path. Authors who want a real wait or
423
+ // call tracking can pass `options.sleep` (e.g. a `vi.fn()` spy).
424
+ const sleepImpl = options.sleep ?? (() => { });
425
+ const sleep = (ms) => {
426
+ if (typeof ms !== "number" ||
427
+ Number.isNaN(ms) ||
428
+ !Number.isFinite(ms) ||
429
+ ms < 0) {
430
+ throw new Error("shisho.sleep: ms must be a finite non-negative number");
431
+ }
432
+ sleepImpl(ms);
433
+ };
421
434
  return {
422
435
  dataDir: "/tmp/shisho-mock-data",
436
+ sleep,
423
437
  log,
424
438
  config,
425
439
  http,
package/testing/index.ts CHANGED
@@ -34,10 +34,21 @@ export interface MockShishoOptions {
34
34
  /**
35
35
  * Path-based filesystem mock.
36
36
  * - string values are returned by readTextFile/readFile
37
- * - Buffer values are returned by readFile (as ArrayBuffer)
37
+ * - Uint8Array (or Buffer) values are returned by readFile (as ArrayBuffer)
38
38
  * - string[] values are returned by listDir
39
39
  */
40
- fs?: Record<string, string | Buffer | string[]>;
40
+ fs?: Record<string, string | Uint8Array | string[]>;
41
+ /**
42
+ * Override the `sleep` implementation. Defaults to a no-op so tests asserting
43
+ * exponential backoff with e.g. `shisho.sleep(1000 * Math.pow(2, attempt))`
44
+ * don't actually block for the cumulative wall-clock time. Wrap with your
45
+ * test framework's spy helper (e.g. `vi.fn()`) if you want to assert the
46
+ * arguments each retry passed.
47
+ *
48
+ * The override is still subject to the same validation rules as production
49
+ * (no NaN/Infinity/negative).
50
+ */
51
+ sleep?: (ms: number) => void;
41
52
  }
42
53
 
43
54
  function statusText(status: number): string {
@@ -481,7 +492,6 @@ export function createMockShisho(
481
492
  const encoder = new TextEncoder();
482
493
  return encoder.encode(entry).buffer as ArrayBuffer;
483
494
  }
484
- // Buffer
485
495
  return entry.buffer.slice(
486
496
  entry.byteOffset,
487
497
  entry.byteOffset + entry.byteLength,
@@ -508,7 +518,6 @@ export function createMockShisho(
508
518
  if (typeof entry === "string") {
509
519
  return entry;
510
520
  }
511
- // Buffer -> string
512
521
  const decoder = new TextDecoder();
513
522
  return decoder.decode(entry);
514
523
  },
@@ -562,8 +571,27 @@ export function createMockShisho(
562
571
  );
563
572
  };
564
573
 
574
+ // --- sleep: validate like production, then delegate ---
575
+ // Defaults to a no-op because plugin tests asserting exponential backoff
576
+ // (`shisho.sleep(1000 * Math.pow(2, attempt))`) would otherwise add several
577
+ // seconds of wall-clock per retry path. Authors who want a real wait or
578
+ // call tracking can pass `options.sleep` (e.g. a `vi.fn()` spy).
579
+ const sleepImpl = options.sleep ?? (() => {});
580
+ const sleep: ShishoHostAPI["sleep"] = (ms: number): void => {
581
+ if (
582
+ typeof ms !== "number" ||
583
+ Number.isNaN(ms) ||
584
+ !Number.isFinite(ms) ||
585
+ ms < 0
586
+ ) {
587
+ throw new Error("shisho.sleep: ms must be a finite non-negative number");
588
+ }
589
+ sleepImpl(ms);
590
+ };
591
+
565
592
  return {
566
593
  dataDir: "/tmp/shisho-mock-data",
594
+ sleep,
567
595
  log,
568
596
  config,
569
597
  http,