@rsvp-engine/core 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 5fa61c8: Preserve the last speed input exactly in its supplied unit, including fractional values. For example, setting 225 WPM now returns exactly 225 from the getter and snapshot. Explicit millisecond inputs retain their exact duration and derive WPM without additional rounding. Constructor `msPerItem` precedence and playback timing semantics are unchanged.
8
+
3
9
  ## 0.1.0
4
10
 
5
11
  ### Minor Changes
package/CONTRIBUTING.md CHANGED
@@ -1,52 +1,30 @@
1
1
  # Contributing to `@rsvp-engine/core`
2
2
 
3
- Follow the workspace workflow in [`../../CONTRIBUTING.md`](../../CONTRIBUTING.md) and the Core constraints in [`AGENTS.md`](./AGENTS.md). This guide covers only Core-specific practices.
3
+ Follow the workspace workflow in [`../../CONTRIBUTING.md`](../../CONTRIBUTING.md). This guide contains only Core-specific practices.
4
4
 
5
- ## TypeScript conventions
5
+ ## Understand the Design
6
6
 
7
- - Use `interface` for object contracts and `type` for unions and primitives.
8
- - Declare explicit return types on public functions and methods to prevent accidental contract changes.
9
- - Mark immutable fields and properties `readonly`.
10
- - Use ECMAScript `#privateField` syntax for runtime encapsulation.
11
- - Use `PascalCase` for classes and interfaces, `camelCase` for methods and variables, and `UPPER_SNAKE_CASE` for global constants.
12
-
13
- ## Test strategy
14
-
15
- Use Vitest and write the behavior-defining test before its implementation.
16
-
17
- ### Required coverage
18
-
19
- - Cover every valid state transition and invalid transition behavior.
20
- - Use `vi.useFakeTimers()` for time-dependent tests. Simulate late timer callbacks to verify drift recovery.
21
- - Cover tokenizer boundaries, including empty and whitespace-only input, punctuation, and non-string `Token<T>` arrays.
22
- - Restore mocks and timers and remove event listeners after each test.
7
+ Core must remain dependency-free, platform-neutral, deterministic, and compact. Before changing a long-lived boundary, read the relevant documentation:
23
8
 
24
- ## Verification
9
+ - [Architecture and dependency injection](docs/architecture/README.md)
10
+ - [State machine lifecycle](docs/STATE-MACHINE.md)
11
+ - [Public API reference](docs/API-REFERENCE.md)
12
+ - [Architecture decisions](docs/architecture/adr/README.md)
25
13
 
26
- Run from the repository root:
14
+ Quality thresholds, package outputs, and supported runtimes are encoded in package configuration and validation scripts. Treat those files as the source of truth; the root `pnpm verify` command runs the applicable gates.
27
15
 
28
- ```bash
29
- pnpm --filter @rsvp-engine/core build
30
- pnpm --filter @rsvp-engine/core test
31
- pnpm --filter @rsvp-engine/core test:coverage
32
- pnpm lint
33
- pnpm typecheck
34
- ```
16
+ ## Write TypeScript Deliberately
35
17
 
36
- Before submitting a change, confirm that:
37
-
38
- - the minified and gzipped bundle remains below 5 KB;
39
- - line and branch coverage remain at least 95%;
40
- - timing tests keep simulated one-minute drift below 10 ms;
41
- - public interfaces, classes, and methods have TSDoc comments.
42
-
43
- ## Packaging
44
-
45
- `tsup` produces CommonJS, ESM, and declaration outputs. Before a release, verify these manifest entries:
18
+ - Use `interface` for object contracts and `type` for unions and primitives.
19
+ - Declare explicit return types on public functions and methods.
20
+ - Mark immutable fields and properties `readonly`.
21
+ - Use ECMAScript `#privateField` syntax for runtime encapsulation.
22
+ - Document public interfaces, classes, and methods with TSDoc.
46
23
 
47
- - `main`: `./dist/index.cjs` (CJS)
48
- - `module`: `./dist/index.js` (ESM)
49
- - `types`: `./dist/index.d.ts` (Types)
50
- - `files`: an explicit allowlist of consumer-facing artifacts; exclude source, tests, caches, and repository-only `docs/` content.
24
+ ## Test Behavior
51
25
 
52
- Use the root contribution guide for Changesets, commit, pull request, and release rules.
26
+ - Write the behavior-defining test before its implementation.
27
+ - Cover valid state transitions and invalid-transition behavior.
28
+ - Use fake timers for scheduling tests, including delayed callbacks and drift recovery.
29
+ - Cover tokenizer boundaries such as empty input, whitespace, punctuation, and non-string tokens.
30
+ - Restore timers and mocks and remove event listeners after each test.
package/README.md CHANGED
@@ -60,6 +60,10 @@ const tokens = await tokenizeWithYourService(text);
60
60
  engine.loadTokens(tokens);
61
61
  ```
62
62
 
63
+ ## Speed control
64
+
65
+ Use `engine.setSpeed(225)` to set the reading rate or `engine.setMsPerItem(250)` to set the base display duration. The last supplied value is preserved exactly in its unit, including in snapshots; the other unit is derived without additional rounding. Changes affect subsequently scheduled display periods.
66
+
63
67
  ## Contributing
64
68
 
65
69
  See the [contributor guidelines](./CONTRIBUTING.md) to develop and verify changes locally.
@@ -6,7 +6,7 @@ import type { TokenizerStrategy } from "../tokenizer/types";
6
6
  export interface RSVPEngineOptions<T = string> {
7
7
  /** Raw text string, single generic item, or generic item array. */
8
8
  readonly data?: T | T[];
9
- /** Target speed in Words Per Minute. Defaults to 300. */
9
+ /** Target WPM, preserved exactly unless msPerItem is supplied. Defaults to 300. */
10
10
  readonly wpm?: number;
11
11
  /** Direct ms-per-item override. Takes precedence over `wpm`. */
12
12
  readonly msPerItem?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/engine/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,MAAM;IAC3C,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC1C,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC;IACvC,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;CAClC;AAED,0DAA0D;AAC1D,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,0DAA0D;AAC1D,eAAO,MAAM,OAAO,OAAQ,CAAC;AAC7B,iEAAiE;AACjE,eAAO,MAAM,eAAe,QAAmB,CAAC;AAChD,iEAAiE;AACjE,eAAO,MAAM,eAAe,QAAmB,CAAC;AAChD,kDAAkD;AAClD,eAAO,MAAM,WAAW,MAAM,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/engine/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,MAAM;IAC3C,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC1C,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC;IACvC,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;CAClC;AAED,0DAA0D;AAC1D,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,0DAA0D;AAC1D,eAAO,MAAM,OAAO,OAAQ,CAAC;AAC7B,iEAAiE;AACjE,eAAO,MAAM,eAAe,QAAmB,CAAC;AAChD,iEAAiE;AACjE,eAAO,MAAM,eAAe,QAAmB,CAAC;AAChD,kDAAkD;AAClD,eAAO,MAAM,WAAW,MAAM,CAAC"}
@@ -57,18 +57,20 @@ export declare class RSVPEngine<T = string> {
57
57
  */
58
58
  destroy(): void;
59
59
  /**
60
- * Updates playback speed via WPM (live, mid-playback).
60
+ * Sets WPM for subsequently scheduled display periods, preserving the input exactly.
61
+ * The corresponding ms-per-item interval is derived without additional rounding.
61
62
  */
62
63
  setSpeed(wpm: number): void;
63
64
  /**
64
- * Directly sets ms-per-item interval (live, mid-playback).
65
+ * Sets the interval for subsequently scheduled display periods, preserving the input exactly.
66
+ * The corresponding WPM is derived without additional rounding.
65
67
  */
66
68
  setMsPerItem(ms: number): void;
67
69
  /** Current engine state. */
68
70
  get state(): RSVPState;
69
- /** Current WPM computed from msPerItem. */
71
+ /** Exact WPM input, or the WPM derived from the last ms-per-item input. */
70
72
  get wpm(): number;
71
- /** Current ms-per-item interval. */
73
+ /** Exact ms-per-item input, or the interval derived from the last WPM input. */
72
74
  get msPerItem(): number;
73
75
  /** Current token index (0-based). */
74
76
  get currentIndex(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"rsvp-engine.d.ts","sourceRoot":"","sources":["../../src/engine/rsvp-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEjG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAYnE;;;GAGG;AACH,qBAAa,UAAU,CAAC,CAAC,GAAG,MAAM;;gBAcpB,OAAO,GAAE,iBAAiB,CAAC,CAAC,CAAM;IAuB9C;;OAEG;IACI,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI;IAchC;;;OAGG;IACI,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI;IAyH3C;;;;OAIG;IACI,IAAI,IAAI,IAAI;IAyCnB;;OAEG;IACI,KAAK,IAAI,IAAI;IAuBpB;;OAEG;IACI,IAAI,IAAI,IAAI;IAwBnB;;;OAGG;IACI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAoChC;;OAEG;IACI,IAAI,IAAI,IAAI;IAuBnB;;OAEG;IACI,QAAQ,IAAI,IAAI;IAuBvB;;;OAGG;IACI,KAAK,IAAI,IAAI;IAyBpB;;;OAGG;IACI,OAAO,IAAI,IAAI;IAatB;;OAEG;IACI,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAMlC;;OAEG;IACI,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQrC,4BAA4B;IAC5B,IAAW,KAAK,IAAI,SAAS,CAE5B;IAED,2CAA2C;IAC3C,IAAW,GAAG,IAAI,MAAM,CAEvB;IAED,oCAAoC;IACpC,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,qCAAqC;IACrC,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,2DAA2D;IAC3D,IAAW,WAAW,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAE3C;IAED,+BAA+B;IAC/B,IAAW,QAAQ,IAAI,MAAM,CAK5B;IAED,oCAAoC;IACpC,IAAW,UAAU,IAAI,MAAM,CAE9B;IAID;;OAEG;IACI,EAAE,CAAC,CAAC,SAAS,aAAa,EAC/B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAC1C,aAAa;IAOhB;;OAEG;IACI,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC;CAYnC"}
1
+ {"version":3,"file":"rsvp-engine.d.ts","sourceRoot":"","sources":["../../src/engine/rsvp-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEjG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAiBnE;;;GAGG;AACH,qBAAa,UAAU,CAAC,CAAC,GAAG,MAAM;;gBAcpB,OAAO,GAAE,iBAAiB,CAAC,CAAC,CAAM;IAuB9C;;OAEG;IACI,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI;IAchC;;;OAGG;IACI,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI;IAyH3C;;;;OAIG;IACI,IAAI,IAAI,IAAI;IAyCnB;;OAEG;IACI,KAAK,IAAI,IAAI;IAuBpB;;OAEG;IACI,IAAI,IAAI,IAAI;IAwBnB;;;OAGG;IACI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAoChC;;OAEG;IACI,IAAI,IAAI,IAAI;IAuBnB;;OAEG;IACI,QAAQ,IAAI,IAAI;IAuBvB;;;OAGG;IACI,KAAK,IAAI,IAAI;IAyBpB;;;OAGG;IACI,OAAO,IAAI,IAAI;IAatB;;;OAGG;IACI,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAMlC;;;OAGG;IACI,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQrC,4BAA4B;IAC5B,IAAW,KAAK,IAAI,SAAS,CAE5B;IAED,2EAA2E;IAC3E,IAAW,GAAG,IAAI,MAAM,CAEvB;IAED,gFAAgF;IAChF,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,qCAAqC;IACrC,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,2DAA2D;IAC3D,IAAW,WAAW,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAE3C;IAED,+BAA+B;IAC/B,IAAW,QAAQ,IAAI,MAAM,CAK5B;IAED,oCAAoC;IACpC,IAAW,UAAU,IAAI,MAAM,CAE9B;IAID;;OAEG;IACI,EAAE,CAAC,CAAC,SAAS,aAAa,EAC/B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAC1C,aAAa;IAOhB;;OAEG;IACI,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC;CAYnC"}
@@ -22,7 +22,9 @@ export interface RSVPSnapshot<T = string> {
22
22
  readonly currentItem: RSVPItem<T> | null;
23
23
  readonly progress: number;
24
24
  readonly totalItems: number;
25
+ /** Exact WPM input, or the WPM derived from the last ms-per-item input. */
25
26
  readonly wpm: number;
27
+ /** Exact ms-per-item input, or the interval derived from the last WPM input. */
26
28
  readonly msPerItem: number;
27
29
  }
28
30
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,MAAM;IAClC,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iGAAiG;IACjG,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM;IACtC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,MAAM;IAClC,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iGAAiG;IACjG,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM;IACtC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var v=1,S=6e3,T=10,b=6e4,E=300;var u=class extends Error{from;action;constructor(e,t){super(`Invalid transition: cannot perform '${t}' from '${e}' state.`),this.name="InvalidTransitionError",this.from=e,this.action=t;}},p=class extends Error{constructor(){super("Cannot interact with a destroyed engine instance."),this.name="EngineDestroyedError";}},o=class extends Error{constructor(e){super(e),this.name="InvalidInputError";}},m=class extends Error{index;totalItems;constructor(e,t){super(`Index ${e} is out of bounds (0..${t-1}).`),this.name="IndexOutOfBoundsError",this.index=e,this.totalItems=t;}};var c=class{#e={stateChange:new Set,itemChange:new Set,complete:new Set,error:new Set};on(e,t){let r=this.#e[e];return r.add(t),()=>{r.delete(t);}}emit(e,t){let r=this.#e[e],n=Array.from(r);for(let s of n)try{s(t);}catch(l){if(e!=="error"){let a=l instanceof Error?l:new Error(String(l));this.emit("error",{error:a});}}}removeAllListeners(){for(let e of Object.values(this.#e))e.clear();}};var h=class{now(){let e=globalThis;return typeof e.performance?.now=="function"?e.performance.now():Date.now()}setTimeout(e,t){let r=globalThis;if(typeof r.setTimeout!="function")throw new TypeError("The runtime does not provide setTimeout.");return r.setTimeout(e,t)}clearTimeout(e){let t=globalThis;if(typeof t.clearTimeout!="function")throw new TypeError("The runtime does not provide clearTimeout.");t.clearTimeout(e);}};var d=class{#e;#t=null;#n=null;constructor(e=new h){this.#e=e;}schedule(e,t){if(!Number.isFinite(t)||t<=0)throw new RangeError("delayMs must be a positive finite number.");this.#t!==null&&this.cancel();let r=this.#e.now(),n=t;if(this.#n===null)this.#n=r+t;else {let s=r-this.#n;s>=t?this.#n=r+t:(this.#n+=t,n=Math.max(t/2,t-s));}this.#t=this.#e.setTimeout(()=>{this.#t=null,e();},n);}cancel(){this.#u(),this.#n=null;}#u(){this.#t!==null&&(this.#e.clearTimeout(this.#t),this.#t=null);}};var f=class{#e="IDLE";#t={IDLE:{load:"IDLE",play:"PLAYING",error:"ERROR"},PLAYING:{pause:"PAUSED",stop:"STOPPED",complete:"COMPLETED",error:"ERROR"},PAUSED:{load:"IDLE",play:"PLAYING",stop:"STOPPED",seek:"PAUSED",error:"ERROR"},STOPPED:{load:"IDLE",play:"PLAYING",seek:"PAUSED",error:"ERROR"},COMPLETED:{load:"IDLE",play:"PLAYING",seek:"PAUSED",error:"ERROR"},ERROR:{reset:"IDLE"}};get state(){return this.#e}transition(e){let r=this.#t[this.#e][e];if(!r)throw new u(this.#e,e);return this.#e=r,this.#e}};var y=class{calculate(e){if(!e)return 0;let t=w(e),r=t.filter(({segment:s})=>!x(s));if(r.length===0)return t[0].index;let n=r.length<=3?0:Math.floor(r.length*.25);return r[n].index}};function w(i){let e=globalThis.Intl?.Segmenter;if(typeof e=="function")return Array.from(new e(void 0,{granularity:"grapheme"}).segment(i));let t=[],r=0;for(let n of i){let s=t.at(-1);s&&(/^[\p{M}\uFE0F]$/u.test(n)||n==="\u200D"||s.segment.endsWith("\u200D"))?t[t.length-1]={index:s.index,segment:s.segment+n}:t.push({index:r,segment:n}),r+=n.length;}return t}function x(i){return /^[\p{P}\p{Z}]+$/u.test(i)}var g=class{#e;#t;#n;#u;#c;constructor(e={}){this.#e=e.sentenceDelay??2,this.#t=e.clauseDelay??1.5,this.#n=e.dashDelay??1.3,this.#u=e.nestedTokenize??false,this.#c=e.ovpStrategy??new y,this.#l();}tokenize(e){return e==null?[]:Array.isArray(e)?this.#i(e):typeof e=="string"?this.#r(e):[{value:e,ovpIndex:0,delayMultiplier:this.#s(e)}]}#i(e){return this.#u?e.flatMap(t=>this.tokenize(t)):e.map(t=>({value:t,ovpIndex:typeof t=="string"?this.#c.calculate(t):0,delayMultiplier:this.#s(t)}))}#r(e){let t=e.trim();return t?this.#o(t).map(n=>({value:n,ovpIndex:this.#c.calculate(n),delayMultiplier:this.#s(n)})):[]}#s(e){return typeof e=="string"?/[.!?。!?…]+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#e:/[,;:,、;:]+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#t:/\p{Pd}+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#n:1:1}#l(){let e=[["sentenceDelay",this.#e],["clauseDelay",this.#t],["dashDelay",this.#n]];for(let[t,r]of e)if(!Number.isFinite(r)||r<=0)throw new o(`${t} must be a positive finite number.`)}#o(e){let t=globalThis.Intl?.Segmenter;if(typeof t!="function")return e.split(/\s+/);let r=new t(void 0,{granularity:"word"}),n=[],s="",l=true;for(let a of r.segment(e))/^\s+$/u.test(a.segment)?(s&&n.push(s),s="",l=true):a.isWordLike||!M(a.segment)?(n.push(s+a.segment),s="",l=false):l||s||/^[\p{Ps}\p{Pi}]+$/u.test(a.segment)?s+=a.segment:n[n.length-1]+=a.segment;return s&&n.push(s),n}};function M(i){return /^\p{P}+$/u.test(i)}function I(i){if(!Array.isArray(i))throw new o("tokens must be an array.");for(let[e,t]of i.entries()){if(typeof t!="object"||t===null)throw new o(`Token ${e} must be an object.`);if(!Number.isInteger(t.ovpIndex)||t.ovpIndex<0)throw new o(`Token ${e} has an invalid ovpIndex.`);if(typeof t.value=="string"&&t.ovpIndex>t.value.length)throw new o(`Token ${e} has an out-of-range ovpIndex.`);if(!Number.isFinite(t.delayMultiplier)||t.delayMultiplier<=0)throw new o(`Token ${e} has an invalid delayMultiplier.`)}}function D(i){if(!Number.isFinite(i)||i<1||i>6e3)throw new o(`wpm must be a finite number between ${1} and ${6e3}.`)}function R(i){if(!Number.isFinite(i)||i<10||i>6e4)throw new o(`msPerItem must be a finite number between ${10} and ${6e4}.`)}var P=class{#e;#t;#n;#u;#c;#i=[];#r=0;#s=false;#l=null;#o=null;#h=6e4/300;#d=false;constructor(e={}){this.#e=new f,this.#t=new c,this.#u=e.timeDriver??new h,this.#n=e.scheduler??new d(this.#u),this.#c=e.tokenizer??new g,e.msPerItem!==void 0?this.setMsPerItem(e.msPerItem):e.wpm!==void 0&&this.setSpeed(e.wpm),e.data!==void 0&&this.load(e.data);}load(e){this.#a(),this.#f();try{this.loadTokens(this.#c.tokenize(e));}catch(t){throw t instanceof o||this.#y(this.#m(t)),t}}loadTokens(e){this.#a(),this.#f(),I(e);let t=this.#e.state;this.#e.transition("load"),this.#n.cancel(),this.#S(e),this.#r=0,this.#s=false,this.#l=null,this.#o=null,t!=="IDLE"&&this.#t.emit("stateChange",{previous:t,current:"IDLE"});}#f(){if(this.#e.state==="PLAYING"||this.#e.state==="ERROR")throw new o(`Cannot load data while engine is ${this.#e.state}.`)}#S(e){this.#i=e.map((t,r)=>({value:t.value,index:r,ovpIndex:t.ovpIndex,delayMultiplier:t.delayMultiplier}));}#y(e){let t=this.#e.state;try{this.#e.transition("error");}catch{this.#p(e);return}this.#n.cancel(),this.#l=null,this.#o=null,this.#p(e),this.#t.emit("stateChange",{previous:t,current:"ERROR"});}#p(e){this.#t.emit("error",{error:e});}#m(e){return e instanceof Error?e:new Error(String(e))}#a(){if(this.#d)throw new p}#T=()=>{if(this.#l=null,this.#o=null,this.#e.state==="PLAYING"){if(this.#r>=this.#i.length-1){this.#b();return}this.#r++,this.#g();}};#g(){let e=this.#i[this.#r];this.#s=true,this.#t.emit("itemChange",{item:e,index:this.#r,progress:this.progress,reason:"playback"}),this.#e.state==="PLAYING"&&this.#v(this.#h*e.delayMultiplier);}#v(e){this.#o=null,this.#l=this.#u.now()+e;try{this.#n.schedule(this.#T,e);}catch(t){this.#y(this.#m(t));}}#b(){this.#n.cancel();let e=this.#e.state;this.#e.transition("complete");let t=this.#i[this.#r];this.#t.emit("stateChange",{previous:e,current:"COMPLETED"}),this.#t.emit("complete",{item:t,totalItems:this.#i.length});}play(){this.#a(),this.#P();}#P(){if(this.#i.length===0){this.#p(new o("Cannot play: no tokens loaded."));return}let e=this.#e.state;(e==="STOPPED"||e==="COMPLETED")&&(this.#r=0);try{this.#e.transition("play");}catch(t){this.#p(this.#m(t));return}if(this.#t.emit("stateChange",{previous:e,current:"PLAYING"}),e==="PAUSED"&&this.#s){let t=this.#i[this.#r];this.#v(this.#o??this.#h*t.delayMultiplier);return}this.#s=false,this.#g();}pause(){this.#a();let e=this.#e.state;try{this.#e.transition("pause");}catch(t){this.#p(this.#m(t));return}this.#o=this.#l===null?null:Math.max(0,this.#l-this.#u.now()),this.#n.cancel(),this.#l=null,this.#t.emit("stateChange",{previous:e,current:"PAUSED"});}stop(){this.#a();let e=this.#e.state;try{this.#e.transition("stop");}catch(t){this.#p(this.#m(t));return}this.#n.cancel(),this.#r=0,this.#s=false,this.#l=null,this.#o=null,this.#t.emit("stateChange",{previous:e,current:"STOPPED"});}seek(e){if(this.#a(),e<0||e>=this.#i.length){this.#p(new m(e,this.#i.length));return}let t=this.#e.state;try{this.#e.transition("seek");}catch(r){this.#p(this.#m(r));return}this.#r=e,this.#s=true,this.#o=this.#h*this.#i[e].delayMultiplier,t!=="PAUSED"&&this.#t.emit("stateChange",{previous:t,current:"PAUSED"}),this.#t.emit("itemChange",{item:this.#i[e],index:e,progress:this.progress,reason:"seek"});}next(){if(this.#a(),this.#e.state!=="PAUSED"){this.#t.emit("error",{error:new o("next() is only available in PAUSED state.")});return}this.#r<this.#i.length-1&&(this.#r++,this.#s=true,this.#o=this.#h*this.#i[this.#r].delayMultiplier,this.#t.emit("itemChange",{item:this.#i[this.#r],index:this.#r,progress:this.progress,reason:"next"}));}previous(){if(this.#a(),this.#e.state!=="PAUSED"){this.#t.emit("error",{error:new o("previous() is only available in PAUSED state.")});return}this.#r>0&&(this.#r--,this.#s=true,this.#o=this.#h*this.#i[this.#r].delayMultiplier,this.#t.emit("itemChange",{item:this.#i[this.#r],index:this.#r,progress:this.progress,reason:"previous"}));}reset(){this.#a();let e=this.#e.state;try{this.#e.transition("reset");}catch(t){this.#p(this.#m(t));return}this.#n.cancel(),this.#i=[],this.#r=0,this.#s=false,this.#l=null,this.#o=null,this.#t.emit("stateChange",{previous:e,current:"IDLE"});}destroy(){this.#d||(this.#n.cancel(),this.#l=null,this.#o=null,this.#t.removeAllListeners(),this.#d=true);}setSpeed(e){this.#a(),D(e),this.#h=6e4/e;}setMsPerItem(e){this.#a(),R(e),this.#h=e;}get state(){return this.#e.state}get wpm(){return 6e4/this.#h}get msPerItem(){return this.#h}get currentIndex(){return this.#r}get currentItem(){return this.#i[this.#r]??null}get progress(){return this.#i.length===0?0:this.#s?(this.#r+1)/this.#i.length:0}get totalItems(){return this.#i.length}on(e,t){return this.#a(),this.#t.on(e,t)}snapshot(){return this.#a(),Object.freeze({state:this.state,currentIndex:this.#r,currentItem:this.currentItem,progress:this.progress,totalItems:this.totalItems,wpm:this.wpm,msPerItem:this.#h})}};exports.DEFAULT_WPM=E;exports.DefaultOVPStrategy=y;exports.DefaultTokenizer=g;exports.DriftCorrectedScheduler=d;exports.EngineDestroyedError=p;exports.EventEmitter=c;exports.IndexOutOfBoundsError=m;exports.InvalidInputError=o;exports.InvalidTransitionError=u;exports.MAX_MS_PER_ITEM=b;exports.MAX_WPM=S;exports.MIN_MS_PER_ITEM=T;exports.MIN_WPM=v;exports.RSVPEngine=P;exports.StateMachine=f;exports.SystemTimeDriver=h;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var g=1,S=6e3,P=10,T=6e4,E=300;var u=class extends Error{from;action;constructor(e,t){super(`Invalid transition: cannot perform '${t}' from '${e}' state.`),this.name="InvalidTransitionError",this.from=e,this.action=t;}},p=class extends Error{constructor(){super("Cannot interact with a destroyed engine instance."),this.name="EngineDestroyedError";}},o=class extends Error{constructor(e){super(e),this.name="InvalidInputError";}},m=class extends Error{index;totalItems;constructor(e,t){super(`Index ${e} is out of bounds (0..${t-1}).`),this.name="IndexOutOfBoundsError",this.index=e,this.totalItems=t;}};var c=class{#e={stateChange:new Set,itemChange:new Set,complete:new Set,error:new Set};on(e,t){let r=this.#e[e];return r.add(t),()=>{r.delete(t);}}emit(e,t){let r=this.#e[e],n=Array.from(r);for(let s of n)try{s(t);}catch(l){if(e!=="error"){let a=l instanceof Error?l:new Error(String(l));this.emit("error",{error:a});}}}removeAllListeners(){for(let e of Object.values(this.#e))e.clear();}};var h=class{now(){let e=globalThis;return typeof e.performance?.now=="function"?e.performance.now():Date.now()}setTimeout(e,t){let r=globalThis;if(typeof r.setTimeout!="function")throw new TypeError("The runtime does not provide setTimeout.");return r.setTimeout(e,t)}clearTimeout(e){let t=globalThis;if(typeof t.clearTimeout!="function")throw new TypeError("The runtime does not provide clearTimeout.");t.clearTimeout(e);}};var d=class{#e;#t=null;#n=null;constructor(e=new h){this.#e=e;}schedule(e,t){if(!Number.isFinite(t)||t<=0)throw new RangeError("delayMs must be a positive finite number.");this.#t!==null&&this.cancel();let r=this.#e.now(),n=t;if(this.#n===null)this.#n=r+t;else {let s=r-this.#n;s>=t?this.#n=r+t:(this.#n+=t,n=Math.max(t/2,t-s));}this.#t=this.#e.setTimeout(()=>{this.#t=null,e();},n);}cancel(){this.#h(),this.#n=null;}#h(){this.#t!==null&&(this.#e.clearTimeout(this.#t),this.#t=null);}};var f=class{#e="IDLE";#t={IDLE:{load:"IDLE",play:"PLAYING",error:"ERROR"},PLAYING:{pause:"PAUSED",stop:"STOPPED",complete:"COMPLETED",error:"ERROR"},PAUSED:{load:"IDLE",play:"PLAYING",stop:"STOPPED",seek:"PAUSED",error:"ERROR"},STOPPED:{load:"IDLE",play:"PLAYING",seek:"PAUSED",error:"ERROR"},COMPLETED:{load:"IDLE",play:"PLAYING",seek:"PAUSED",error:"ERROR"},ERROR:{reset:"IDLE"}};get state(){return this.#e}transition(e){let r=this.#t[this.#e][e];if(!r)throw new u(this.#e,e);return this.#e=r,this.#e}};var y=class{calculate(e){if(!e)return 0;let t=R(e),r=t.filter(({segment:s})=>!x(s));if(r.length===0)return t[0].index;let n=r.length<=3?0:Math.floor(r.length*.25);return r[n].index}};function R(i){let e=globalThis.Intl?.Segmenter;if(typeof e=="function")return Array.from(new e(void 0,{granularity:"grapheme"}).segment(i));let t=[],r=0;for(let n of i){let s=t.at(-1);s&&(/^[\p{M}\uFE0F]$/u.test(n)||n==="\u200D"||s.segment.endsWith("\u200D"))?t[t.length-1]={index:s.index,segment:s.segment+n}:t.push({index:r,segment:n}),r+=n.length;}return t}function x(i){return /^[\p{P}\p{Z}]+$/u.test(i)}var v=class{#e;#t;#n;#h;#c;constructor(e={}){this.#e=e.sentenceDelay??2,this.#t=e.clauseDelay??1.5,this.#n=e.dashDelay??1.3,this.#h=e.nestedTokenize??false,this.#c=e.ovpStrategy??new y,this.#l();}tokenize(e){return e==null?[]:Array.isArray(e)?this.#i(e):typeof e=="string"?this.#r(e):[{value:e,ovpIndex:0,delayMultiplier:this.#s(e)}]}#i(e){return this.#h?e.flatMap(t=>this.tokenize(t)):e.map(t=>({value:t,ovpIndex:typeof t=="string"?this.#c.calculate(t):0,delayMultiplier:this.#s(t)}))}#r(e){let t=e.trim();return t?this.#o(t).map(n=>({value:n,ovpIndex:this.#c.calculate(n),delayMultiplier:this.#s(n)})):[]}#s(e){return typeof e=="string"?/[.!?。!?…]+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#e:/[,;:,、;:]+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#t:/\p{Pd}+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#n:1:1}#l(){let e=[["sentenceDelay",this.#e],["clauseDelay",this.#t],["dashDelay",this.#n]];for(let[t,r]of e)if(!Number.isFinite(r)||r<=0)throw new o(`${t} must be a positive finite number.`)}#o(e){let t=globalThis.Intl?.Segmenter;if(typeof t!="function")return e.split(/\s+/);let r=new t(void 0,{granularity:"word"}),n=[],s="",l=true;for(let a of r.segment(e))/^\s+$/u.test(a.segment)?(s&&n.push(s),s="",l=true):a.isWordLike||!M(a.segment)?(n.push(s+a.segment),s="",l=false):l||s||/^[\p{Ps}\p{Pi}]+$/u.test(a.segment)?s+=a.segment:n[n.length-1]+=a.segment;return s&&n.push(s),n}};function M(i){return /^\p{P}+$/u.test(i)}function I(i){if(!Array.isArray(i))throw new o("tokens must be an array.");for(let[e,t]of i.entries()){if(typeof t!="object"||t===null)throw new o(`Token ${e} must be an object.`);if(!Number.isInteger(t.ovpIndex)||t.ovpIndex<0)throw new o(`Token ${e} has an invalid ovpIndex.`);if(typeof t.value=="string"&&t.ovpIndex>t.value.length)throw new o(`Token ${e} has an out-of-range ovpIndex.`);if(!Number.isFinite(t.delayMultiplier)||t.delayMultiplier<=0)throw new o(`Token ${e} has an invalid delayMultiplier.`)}}function D(i){if(!Number.isFinite(i)||i<1||i>6e3)throw new o(`wpm must be a finite number between ${1} and ${6e3}.`)}function w(i){if(!Number.isFinite(i)||i<10||i>6e4)throw new o(`msPerItem must be a finite number between ${10} and ${6e4}.`)}var b=class{#e;#t;#n;#h;#c;#i=[];#r=0;#s=false;#l=null;#o=null;#p={unit:"wpm",value:300};#d=false;constructor(e={}){this.#e=new f,this.#t=new c,this.#h=e.timeDriver??new h,this.#n=e.scheduler??new d(this.#h),this.#c=e.tokenizer??new v,e.msPerItem!==void 0?this.setMsPerItem(e.msPerItem):e.wpm!==void 0&&this.setSpeed(e.wpm),e.data!==void 0&&this.load(e.data);}load(e){this.#a(),this.#f();try{this.loadTokens(this.#c.tokenize(e));}catch(t){throw t instanceof o||this.#y(this.#m(t)),t}}loadTokens(e){this.#a(),this.#f(),I(e);let t=this.#e.state;this.#e.transition("load"),this.#n.cancel(),this.#S(e),this.#r=0,this.#s=false,this.#l=null,this.#o=null,t!=="IDLE"&&this.#t.emit("stateChange",{previous:t,current:"IDLE"});}#f(){if(this.#e.state==="PLAYING"||this.#e.state==="ERROR")throw new o(`Cannot load data while engine is ${this.#e.state}.`)}#S(e){this.#i=e.map((t,r)=>({value:t.value,index:r,ovpIndex:t.ovpIndex,delayMultiplier:t.delayMultiplier}));}#y(e){let t=this.#e.state;try{this.#e.transition("error");}catch{this.#u(e);return}this.#n.cancel(),this.#l=null,this.#o=null,this.#u(e),this.#t.emit("stateChange",{previous:t,current:"ERROR"});}#u(e){this.#t.emit("error",{error:e});}#m(e){return e instanceof Error?e:new Error(String(e))}#a(){if(this.#d)throw new p}#P=()=>{if(this.#l=null,this.#o=null,this.#e.state==="PLAYING"){if(this.#r>=this.#i.length-1){this.#T();return}this.#r++,this.#v();}};#v(){let e=this.#i[this.#r];this.#s=true,this.#t.emit("itemChange",{item:e,index:this.#r,progress:this.progress,reason:"playback"}),this.#e.state==="PLAYING"&&this.#g(this.msPerItem*e.delayMultiplier);}#g(e){this.#o=null,this.#l=this.#h.now()+e;try{this.#n.schedule(this.#P,e);}catch(t){this.#y(this.#m(t));}}#T(){this.#n.cancel();let e=this.#e.state;this.#e.transition("complete");let t=this.#i[this.#r];this.#t.emit("stateChange",{previous:e,current:"COMPLETED"}),this.#t.emit("complete",{item:t,totalItems:this.#i.length});}play(){this.#a(),this.#b();}#b(){if(this.#i.length===0){this.#u(new o("Cannot play: no tokens loaded."));return}let e=this.#e.state;(e==="STOPPED"||e==="COMPLETED")&&(this.#r=0);try{this.#e.transition("play");}catch(t){this.#u(this.#m(t));return}if(this.#t.emit("stateChange",{previous:e,current:"PLAYING"}),e==="PAUSED"&&this.#s){let t=this.#i[this.#r];this.#g(this.#o??this.msPerItem*t.delayMultiplier);return}this.#s=false,this.#v();}pause(){this.#a();let e=this.#e.state;try{this.#e.transition("pause");}catch(t){this.#u(this.#m(t));return}this.#o=this.#l===null?null:Math.max(0,this.#l-this.#h.now()),this.#n.cancel(),this.#l=null,this.#t.emit("stateChange",{previous:e,current:"PAUSED"});}stop(){this.#a();let e=this.#e.state;try{this.#e.transition("stop");}catch(t){this.#u(this.#m(t));return}this.#n.cancel(),this.#r=0,this.#s=false,this.#l=null,this.#o=null,this.#t.emit("stateChange",{previous:e,current:"STOPPED"});}seek(e){if(this.#a(),e<0||e>=this.#i.length){this.#u(new m(e,this.#i.length));return}let t=this.#e.state;try{this.#e.transition("seek");}catch(r){this.#u(this.#m(r));return}this.#r=e,this.#s=true,this.#o=this.msPerItem*this.#i[e].delayMultiplier,t!=="PAUSED"&&this.#t.emit("stateChange",{previous:t,current:"PAUSED"}),this.#t.emit("itemChange",{item:this.#i[e],index:e,progress:this.progress,reason:"seek"});}next(){if(this.#a(),this.#e.state!=="PAUSED"){this.#t.emit("error",{error:new o("next() is only available in PAUSED state.")});return}this.#r<this.#i.length-1&&(this.#r++,this.#s=true,this.#o=this.msPerItem*this.#i[this.#r].delayMultiplier,this.#t.emit("itemChange",{item:this.#i[this.#r],index:this.#r,progress:this.progress,reason:"next"}));}previous(){if(this.#a(),this.#e.state!=="PAUSED"){this.#t.emit("error",{error:new o("previous() is only available in PAUSED state.")});return}this.#r>0&&(this.#r--,this.#s=true,this.#o=this.msPerItem*this.#i[this.#r].delayMultiplier,this.#t.emit("itemChange",{item:this.#i[this.#r],index:this.#r,progress:this.progress,reason:"previous"}));}reset(){this.#a();let e=this.#e.state;try{this.#e.transition("reset");}catch(t){this.#u(this.#m(t));return}this.#n.cancel(),this.#i=[],this.#r=0,this.#s=false,this.#l=null,this.#o=null,this.#t.emit("stateChange",{previous:e,current:"IDLE"});}destroy(){this.#d||(this.#n.cancel(),this.#l=null,this.#o=null,this.#t.removeAllListeners(),this.#d=true);}setSpeed(e){this.#a(),D(e),this.#p={unit:"wpm",value:e};}setMsPerItem(e){this.#a(),w(e),this.#p={unit:"msPerItem",value:e};}get state(){return this.#e.state}get wpm(){return this.#p.unit==="wpm"?this.#p.value:6e4/this.#p.value}get msPerItem(){return this.#p.unit==="msPerItem"?this.#p.value:6e4/this.#p.value}get currentIndex(){return this.#r}get currentItem(){return this.#i[this.#r]??null}get progress(){return this.#i.length===0?0:this.#s?(this.#r+1)/this.#i.length:0}get totalItems(){return this.#i.length}on(e,t){return this.#a(),this.#t.on(e,t)}snapshot(){return this.#a(),Object.freeze({state:this.state,currentIndex:this.#r,currentItem:this.currentItem,progress:this.progress,totalItems:this.totalItems,wpm:this.wpm,msPerItem:this.msPerItem})}};exports.DEFAULT_WPM=E;exports.DefaultOVPStrategy=y;exports.DefaultTokenizer=v;exports.DriftCorrectedScheduler=d;exports.EngineDestroyedError=p;exports.EventEmitter=c;exports.IndexOutOfBoundsError=m;exports.InvalidInputError=o;exports.InvalidTransitionError=u;exports.MAX_MS_PER_ITEM=T;exports.MAX_WPM=S;exports.MIN_MS_PER_ITEM=P;exports.MIN_WPM=g;exports.RSVPEngine=b;exports.StateMachine=f;exports.SystemTimeDriver=h;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine/config.ts","../src/errors.ts","../src/events/event-emitter.ts","../src/scheduler/system-time-driver.ts","../src/scheduler/drift-corrected-scheduler.ts","../src/state/state-machine.ts","../src/tokenizer/ovp.ts","../src/tokenizer/default-tokenizer.ts","../src/engine/validation.ts","../src/engine/rsvp-engine.ts"],"names":["MIN_WPM","MAX_WPM","MIN_MS_PER_ITEM","MAX_MS_PER_ITEM","DEFAULT_WPM","InvalidTransitionError","from","action","EngineDestroyedError","InvalidInputError","message","IndexOutOfBoundsError","index","totalItems","EventEmitter","#listenerRegistry","event","callback","eventListeners","payload","listenersSnapshot","listener","err","error","SystemTimeDriver","runtimeGlobal","ms","handle","DriftCorrectedScheduler","#timeDriver","#timeoutHandle","#expectedTime","timeDriver","task","delayMs","now","actualDelay","drift","#clearTimer","StateMachine","#state","#transitions","nextState","DefaultOVPStrategy","text","graphemes","segmentGraphemes","content","segment","isPunctuation","target","Segmenter","previous","value","DefaultTokenizer","#sentenceDelay","#clauseDelay","#dashDelay","#nestedTokenize","#ovpStrategy","options","#validateOptions","input","#tokenizeArray","#tokenizeText","#calculateDelay","items","item","trimmed","#segmentWords","word","name","segmenter","tokens","prefix","atBoundary","part","validateTokens","token","validateWpm","wpm","validateMsPerItem","RSVPEngine","#stateMachine","#emitter","#scheduler","#tokenizer","#tokens","#currentIndex","#hasPresentedCurrent","#deadline","#remainingDelay","#msPerItem","#destroyed","data","#assertNotDestroyed","#assertCanLoad","#enterFatalError","#toError","previousState","#setTokens","#reportError","#advance","#completePlayback","#presentCurrent","#scheduleAdvance","delay","#startPlayback"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { SchedulerStrategy, TimeDriver } from \"../scheduler/types\";\nimport type { TokenizerStrategy } from \"../tokenizer/types\";\n\n/**\n * Configuration options for the RSVP Engine.\n */\nexport interface RSVPEngineOptions<T = string> {\n /** Raw text string, single generic item, or generic item array. */\n readonly data?: T | T[];\n /** Target speed in Words Per Minute. Defaults to 300. */\n readonly wpm?: number;\n /** Direct ms-per-item override. Takes precedence over `wpm`. */\n readonly msPerItem?: number;\n /** Custom tokenizer strategy. Defaults to `DefaultTokenizer`. */\n readonly tokenizer?: TokenizerStrategy<T>;\n /** Custom scheduler strategy. Defaults to `DriftCorrectedScheduler`. */\n readonly scheduler?: SchedulerStrategy;\n /** Clock used to preserve remaining display time across pause/resume. */\n readonly timeDriver?: TimeDriver;\n}\n\n/** Minimum allowed playback speed in Words Per Minute. */\nexport const MIN_WPM = 1;\n/** Maximum allowed playback speed in Words Per Minute. */\nexport const MAX_WPM = 6_000;\n/** Minimum allowed display interval per item in milliseconds. */\nexport const MIN_MS_PER_ITEM = 60_000 / MAX_WPM;\n/** Maximum allowed display interval per item in milliseconds. */\nexport const MAX_MS_PER_ITEM = 60_000 / MIN_WPM;\n/** Default playback speed in Words Per Minute. */\nexport const DEFAULT_WPM = 300;\n","/**\n * Thrown when an invalid state transition is attempted.\n *\n */\nexport class InvalidTransitionError extends Error {\n public readonly from: string;\n public readonly action: string;\n\n constructor(from: string, action: string) {\n super(`Invalid transition: cannot perform '${action}' from '${from}' state.`);\n this.name = \"InvalidTransitionError\";\n this.from = from;\n this.action = action;\n }\n}\n\n/**\n * Thrown when a method is called on a destroyed engine instance.\n */\nexport class EngineDestroyedError extends Error {\n constructor() {\n super(\"Cannot interact with a destroyed engine instance.\");\n this.name = \"EngineDestroyedError\";\n }\n}\n\n/**\n * Thrown when invalid input is provided to the engine (empty text, null data, etc.).\n */\nexport class InvalidInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidInputError\";\n }\n}\n\n/**\n * Thrown when a seek index is out of bounds.\n */\nexport class IndexOutOfBoundsError extends Error {\n public readonly index: number;\n public readonly totalItems: number;\n\n constructor(index: number, totalItems: number) {\n super(`Index ${index} is out of bounds (0..${totalItems - 1}).`);\n this.name = \"IndexOutOfBoundsError\";\n this.index = index;\n this.totalItems = totalItems;\n }\n}\n","import type { EventCallback, RSVPEventMap, RSVPEventType, UnsubscribeFn } from \"./types\";\n\ntype ListenerRegistry<T> = {\n [K in RSVPEventType]: Set<EventCallback<RSVPEventMap<T>[K]>>;\n};\n\nexport class EventEmitter<T = string> {\n readonly #listenerRegistry: ListenerRegistry<T> = {\n stateChange: new Set(),\n itemChange: new Set(),\n complete: new Set(),\n error: new Set(),\n };\n\n public on<K extends RSVPEventType>(\n event: K,\n callback: EventCallback<RSVPEventMap<T>[K]>,\n ): UnsubscribeFn {\n const eventListeners = this.#listenerRegistry[event];\n eventListeners.add(callback);\n\n return () => {\n eventListeners.delete(callback);\n };\n }\n\n public emit<K extends RSVPEventType>(event: K, payload: RSVPEventMap<T>[K]): void {\n const eventListeners = this.#listenerRegistry[event];\n\n // Copy to array to handle safe iteration if unsubscribed during emit\n const listenersSnapshot = Array.from(eventListeners);\n\n for (const listener of listenersSnapshot) {\n try {\n listener(payload);\n } catch (err) {\n if (event !== \"error\") {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit(\"error\", { error });\n }\n }\n }\n }\n\n public removeAllListeners(): void {\n for (const eventListeners of Object.values(this.#listenerRegistry)) {\n eventListeners.clear();\n }\n }\n}\n","import type { TimeDriver } from \"./types\";\nimport type { RuntimeGlobal } from \"../runtimeGlobal\";\n\n/**\n * Default implementation of `TimeDriver` utilizing platform globals\n * (`performance.now` / `Date.now` and `setTimeout` / `clearTimeout`).\n */\nexport class SystemTimeDriver implements TimeDriver {\n public now(): number {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.performance?.now === \"function\") {\n return runtimeGlobal.performance.now();\n }\n return Date.now();\n }\n\n public setTimeout(callback: () => void, ms: number): unknown {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.setTimeout !== \"function\") {\n throw new TypeError(\"The runtime does not provide setTimeout.\");\n }\n return runtimeGlobal.setTimeout(callback, ms);\n }\n\n public clearTimeout(handle: unknown): void {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.clearTimeout !== \"function\") {\n throw new TypeError(\"The runtime does not provide clearTimeout.\");\n }\n runtimeGlobal.clearTimeout(handle);\n }\n}\n","import type { SchedulerStrategy, TimeDriver } from \"./types\";\n\nimport { SystemTimeDriver } from \"./system-time-driver\";\n\n/**\n * High-precision, drift-corrected task scheduler.\n *\n * Implements the {@link SchedulerStrategy} interface by tracking an ideal target timeline\n * (`#expectedTime`) across consecutive task executions. Any latency or jitter introduced\n * by the JavaScript event loop (timer drift) is calculated and subtracted from subsequent\n * intervals, ensuring stable WPM presentation without cumulative time slippage.\n *\n * @example\n * ```ts\n * const scheduler = new DriftCorrectedScheduler();\n *\n * // Schedule a task after 200ms\n * scheduler.schedule(() => {\n * console.log(\"Tick!\");\n * }, 200);\n *\n * // Cancel any pending task\n * scheduler.cancel();\n * ```\n */\nexport class DriftCorrectedScheduler implements SchedulerStrategy {\n readonly #timeDriver: TimeDriver;\n #timeoutHandle: unknown = null;\n #expectedTime: number | null = null;\n\n /**\n * Creates a new instance of `DriftCorrectedScheduler`.\n *\n * @param timeDriver - The underlying time and timer provider. Defaults to {@link SystemTimeDriver}.\n */\n constructor(timeDriver: TimeDriver = new SystemTimeDriver()) {\n this.#timeDriver = timeDriver;\n }\n\n /**\n * Schedules a task to execute after the given delay, compensating for accumulated drift.\n *\n * If a previous task is currently pending, it is automatically cancelled before scheduling\n * the new one. When part of an active session, the actual timeout duration is adjusted\n * by subtracting any detected execution delay (`drift = now - expectedTime`).\n *\n * @param task - The callback function to execute.\n * @param delayMs - The target delay in milliseconds.\n */\n public schedule(task: () => void, delayMs: number): void {\n if (!Number.isFinite(delayMs) || delayMs <= 0) {\n throw new RangeError(\"delayMs must be a positive finite number.\");\n }\n\n if (this.#timeoutHandle !== null) {\n this.cancel();\n }\n\n const now = this.#timeDriver.now();\n let actualDelay = delayMs;\n\n if (this.#expectedTime === null) {\n this.#expectedTime = now + delayMs;\n } else {\n const drift = now - this.#expectedTime;\n if (drift >= delayMs) {\n // Large catch-up bursts make RSVP items unreadable; start a fresh timeline instead.\n this.#expectedTime = now + delayMs;\n } else {\n this.#expectedTime += delayMs;\n actualDelay = Math.max(delayMs / 2, delayMs - drift);\n }\n }\n\n this.#timeoutHandle = this.#timeDriver.setTimeout(() => {\n this.#timeoutHandle = null;\n task();\n }, actualDelay);\n }\n\n /**\n * Cancels any pending scheduled task and resets the session timeline.\n *\n * Calling this method halts the drift compensation timeline so that the next\n * call to {@link schedule} starts a fresh session without previous drift history.\n */\n public cancel(): void {\n this.#clearTimer();\n this.#expectedTime = null;\n }\n\n #clearTimer(): void {\n if (this.#timeoutHandle !== null) {\n this.#timeDriver.clearTimeout(this.#timeoutHandle);\n this.#timeoutHandle = null;\n }\n }\n}\n","import type { RSVPState, StateMachineAction } from \"./types\";\n\nimport { InvalidTransitionError } from \"../errors\";\n\nexport class StateMachine {\n #state: RSVPState = \"IDLE\";\n\n readonly #transitions: Record<RSVPState, Partial<Record<StateMachineAction, RSVPState>>> = {\n IDLE: {\n load: \"IDLE\",\n play: \"PLAYING\",\n error: \"ERROR\",\n },\n PLAYING: {\n pause: \"PAUSED\",\n stop: \"STOPPED\",\n complete: \"COMPLETED\",\n error: \"ERROR\",\n },\n PAUSED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n stop: \"STOPPED\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n STOPPED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n COMPLETED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n ERROR: {\n reset: \"IDLE\",\n },\n };\n\n public get state(): RSVPState {\n return this.#state;\n }\n\n public transition(action: StateMachineAction): RSVPState {\n const allowedTransitions = this.#transitions[this.#state];\n const nextState = allowedTransitions[action];\n\n if (!nextState) {\n throw new InvalidTransitionError(this.#state, action);\n }\n\n this.#state = nextState;\n return this.#state;\n }\n}\n","import type { OVPStrategy } from \"./types\";\n\n/**\n * Default grapheme-aware Optimal Viewing Position strategy.\n */\nexport class DefaultOVPStrategy implements OVPStrategy {\n /**\n * Calculates the Optimal Viewing Position for text.\n * @returns The zero-based UTF-16 offset of the OVP grapheme.\n */\n public calculate(text: string): number {\n if (!text) {\n return 0;\n }\n\n const graphemes = segmentGraphemes(text);\n const content = graphemes.filter(({ segment }) => !isPunctuation(segment));\n\n if (content.length === 0) {\n return graphemes[0].index;\n }\n\n const target = content.length <= 3 ? 0 : Math.floor(content.length * 0.25);\n return content[target].index;\n }\n}\n\ninterface Grapheme {\n readonly index: number;\n readonly segment: string;\n}\n\nfunction segmentGraphemes(text: string): Grapheme[] {\n const Segmenter = globalThis.Intl?.Segmenter;\n if (typeof Segmenter === \"function\") {\n return Array.from(new Segmenter(undefined, { granularity: \"grapheme\" }).segment(text));\n }\n\n const graphemes: Grapheme[] = [];\n let index = 0;\n for (const segment of text) {\n const previous = graphemes.at(-1);\n if (\n previous &&\n (/^[\\p{M}\\uFE0F]$/u.test(segment) ||\n segment === \"\\u200D\" ||\n previous.segment.endsWith(\"\\u200D\"))\n ) {\n graphemes[graphemes.length - 1] = {\n index: previous.index,\n segment: previous.segment + segment,\n };\n } else {\n graphemes.push({ index, segment });\n }\n index += segment.length;\n }\n return graphemes;\n}\n\nfunction isPunctuation(value: string): boolean {\n return /^[\\p{P}\\p{Z}]+$/u.test(value);\n}\n","import type { OVPStrategy, Token, TokenizerStrategy } from \"./types\";\n\nimport { InvalidInputError } from \"../errors\";\n\nimport { DefaultOVPStrategy } from \"./ovp\";\n\n/**\n * Options for configuring `DefaultTokenizer`.\n */\nexport interface DefaultTokenizerOptions {\n /** Multiplier for sentence-ending punctuation (`.`, `!`, `?`). Defaults to `2.0`. */\n readonly sentenceDelay?: number;\n /** Multiplier for clause-ending punctuation (`,`, `;`, `:`). Defaults to `1.5`. */\n readonly clauseDelay?: number;\n /** Multiplier for dashes (`—`, `–`). Defaults to `1.3`. */\n readonly dashDelay?: number;\n /** Whether array elements and nested inputs should be recursively tokenized. Defaults to `false`. */\n readonly nestedTokenize?: boolean;\n /** Strategy used to calculate viewing positions for string tokens. */\n readonly ovpStrategy?: OVPStrategy;\n}\n\n/**\n * Default configurable tokenizer for text, items, and array inputs.\n * Splits text on whitespace and assigns delay multipliers based on punctuation.\n * Wraps custom generic items or arrays into presentable tokens.\n * @typeParam T - The type of items being presented (defaults to `string`).\n */\nexport class DefaultTokenizer<T> implements TokenizerStrategy<T> {\n readonly #sentenceDelay: number;\n readonly #clauseDelay: number;\n readonly #dashDelay: number;\n readonly #nestedTokenize: boolean;\n readonly #ovpStrategy: OVPStrategy;\n\n constructor(options: DefaultTokenizerOptions = {}) {\n this.#sentenceDelay = options.sentenceDelay ?? 2.0;\n this.#clauseDelay = options.clauseDelay ?? 1.5;\n this.#dashDelay = options.dashDelay ?? 1.3;\n this.#nestedTokenize = options.nestedTokenize ?? false;\n this.#ovpStrategy = options.ovpStrategy ?? new DefaultOVPStrategy();\n this.#validateOptions();\n }\n\n public tokenize(input: T | T[]): Token<T>[] {\n if (input === null || input === undefined) {\n return [];\n }\n\n if (Array.isArray(input)) {\n return this.#tokenizeArray(input);\n }\n\n if (typeof input === \"string\") {\n return this.#tokenizeText(input) as Token<T>[];\n }\n\n return [\n {\n value: input,\n ovpIndex: 0,\n delayMultiplier: this.#calculateDelay(input),\n },\n ];\n }\n\n #tokenizeArray(items: T[]): Token<T>[] {\n if (this.#nestedTokenize) {\n return items.flatMap((item) => this.tokenize(item));\n }\n\n return items.map((item) => ({\n value: item,\n ovpIndex: typeof item === \"string\" ? this.#ovpStrategy.calculate(item) : 0,\n delayMultiplier: this.#calculateDelay(item),\n }));\n }\n\n #tokenizeText(text: string): Token<string>[] {\n const trimmed = text.trim();\n if (!trimmed) {\n return [];\n }\n\n const words = this.#segmentWords(trimmed);\n return words.map((word) => ({\n value: word,\n ovpIndex: this.#ovpStrategy.calculate(word),\n delayMultiplier: this.#calculateDelay(word),\n }));\n }\n\n #calculateDelay(item: unknown): number {\n if (typeof item === \"string\") {\n if (/[.!?。!?…]+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#sentenceDelay;\n }\n if (/[,;:,、;:]+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#clauseDelay;\n }\n if (/\\p{Pd}+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#dashDelay;\n }\n return 1.0;\n }\n\n return 1.0;\n }\n\n #validateOptions(): void {\n const options = [\n [\"sentenceDelay\", this.#sentenceDelay],\n [\"clauseDelay\", this.#clauseDelay],\n [\"dashDelay\", this.#dashDelay],\n ] as const;\n for (const [name, value] of options) {\n if (!Number.isFinite(value) || value <= 0) {\n throw new InvalidInputError(`${name} must be a positive finite number.`);\n }\n }\n }\n\n #segmentWords(text: string): string[] {\n const Segmenter = globalThis.Intl?.Segmenter;\n if (typeof Segmenter !== \"function\") {\n return text.split(/\\s+/);\n }\n\n const segmenter = new Segmenter(undefined, { granularity: \"word\" });\n const tokens: string[] = [];\n let prefix = \"\";\n let atBoundary = true;\n for (const part of segmenter.segment(text)) {\n if (/^\\s+$/u.test(part.segment)) {\n if (prefix) {\n tokens.push(prefix);\n }\n prefix = \"\";\n atBoundary = true;\n } else if (part.isWordLike || !isPunctuation(part.segment)) {\n tokens.push(prefix + part.segment);\n prefix = \"\";\n atBoundary = false;\n } else if (atBoundary || prefix || /^[\\p{Ps}\\p{Pi}]+$/u.test(part.segment)) {\n prefix += part.segment;\n } else {\n tokens[tokens.length - 1] += part.segment;\n }\n }\n if (prefix) {\n tokens.push(prefix);\n }\n return tokens;\n }\n}\n\nfunction isPunctuation(value: string): boolean {\n return /^\\p{P}+$/u.test(value);\n}\n","import type { Token } from \"../tokenizer/types\";\n\nimport { InvalidInputError } from \"../errors\";\n\nimport { MAX_MS_PER_ITEM, MAX_WPM, MIN_MS_PER_ITEM, MIN_WPM } from \"./config\";\n\nexport function validateTokens<T>(tokens: Token<T>[]): void {\n if (!Array.isArray(tokens)) {\n throw new InvalidInputError(\"tokens must be an array.\");\n }\n\n for (const [index, token] of tokens.entries()) {\n if (typeof token !== \"object\" || token === null) {\n throw new InvalidInputError(`Token ${index} must be an object.`);\n }\n if (!Number.isInteger(token.ovpIndex) || token.ovpIndex < 0) {\n throw new InvalidInputError(`Token ${index} has an invalid ovpIndex.`);\n }\n if (typeof token.value === \"string\" && token.ovpIndex > token.value.length) {\n throw new InvalidInputError(`Token ${index} has an out-of-range ovpIndex.`);\n }\n if (!Number.isFinite(token.delayMultiplier) || token.delayMultiplier <= 0) {\n throw new InvalidInputError(`Token ${index} has an invalid delayMultiplier.`);\n }\n }\n}\n\nexport function validateWpm(wpm: number): void {\n if (!Number.isFinite(wpm) || wpm < MIN_WPM || wpm > MAX_WPM) {\n throw new InvalidInputError(`wpm must be a finite number between ${MIN_WPM} and ${MAX_WPM}.`);\n }\n}\n\nexport function validateMsPerItem(ms: number): void {\n if (!Number.isFinite(ms) || ms < MIN_MS_PER_ITEM || ms > MAX_MS_PER_ITEM) {\n throw new InvalidInputError(\n `msPerItem must be a finite number between ${MIN_MS_PER_ITEM} and ${MAX_MS_PER_ITEM}.`,\n );\n }\n}\n","import type { RSVPEngineOptions } from \"./config\";\nimport type { RSVPItem, RSVPSnapshot } from \"./types\";\nimport type { EventCallback, RSVPEventMap, RSVPEventType, UnsubscribeFn } from \"../events/types\";\nimport type { SchedulerStrategy, TimeDriver } from \"../scheduler/types\";\nimport type { RSVPState } from \"../state/types\";\nimport type { Token, TokenizerStrategy } from \"../tokenizer/types\";\n\nimport { EngineDestroyedError, IndexOutOfBoundsError, InvalidInputError } from \"../errors\";\nimport { EventEmitter } from \"../events/event-emitter\";\nimport { DriftCorrectedScheduler } from \"../scheduler/drift-corrected-scheduler\";\nimport { SystemTimeDriver } from \"../scheduler/system-time-driver\";\nimport { StateMachine } from \"../state/state-machine\";\nimport { DefaultTokenizer } from \"../tokenizer/default-tokenizer\";\n\nimport { DEFAULT_WPM } from \"./config\";\nimport { validateMsPerItem, validateTokens, validateWpm } from \"./validation\";\n\n/**\n * Headless RSVP Engine — orchestrates state machine, scheduler, tokenizer, and event emitter.\n * @typeParam T - The type of items being presented (defaults to `string`).\n */\nexport class RSVPEngine<T = string> {\n readonly #stateMachine: StateMachine;\n readonly #emitter: EventEmitter<T>;\n readonly #scheduler: SchedulerStrategy;\n readonly #timeDriver: TimeDriver;\n readonly #tokenizer: TokenizerStrategy<T>;\n #tokens: RSVPItem<T>[] = [];\n #currentIndex = 0;\n #hasPresentedCurrent = false;\n #deadline: number | null = null;\n #remainingDelay: number | null = null;\n #msPerItem: number = 60_000 / DEFAULT_WPM;\n #destroyed = false;\n\n constructor(options: RSVPEngineOptions<T> = {}) {\n this.#stateMachine = new StateMachine();\n this.#emitter = new EventEmitter<T>();\n\n this.#timeDriver = options.timeDriver ?? new SystemTimeDriver();\n this.#scheduler = options.scheduler ?? new DriftCorrectedScheduler(this.#timeDriver);\n this.#tokenizer = options.tokenizer ?? new DefaultTokenizer<T>();\n\n // msPerItem takes precedence over wpm\n if (options.msPerItem !== undefined) {\n this.setMsPerItem(options.msPerItem);\n } else if (options.wpm !== undefined) {\n this.setSpeed(options.wpm);\n }\n\n // Constructor failures must remain observable because listeners cannot be attached yet.\n if (options.data !== undefined) {\n this.load(options.data);\n }\n }\n\n // ────────── Tokenization & Loading ──────────\n\n /**\n * Tokenizes and loads raw data into the engine using the configured tokenizer.\n */\n public load(data: T | T[]): void {\n this.#assertNotDestroyed();\n this.#assertCanLoad();\n try {\n this.loadTokens(this.#tokenizer.tokenize(data));\n } catch (error) {\n if (error instanceof InvalidInputError) {\n throw error;\n }\n this.#enterFatalError(this.#toError(error));\n throw error;\n }\n }\n\n /**\n * Directly loads pre-tokenized items into the engine.\n * Useful when tokenization is performed externally (e.g. AI or asynchronous tokenizers).\n */\n public loadTokens(tokens: Token<T>[]): void {\n this.#assertNotDestroyed();\n this.#assertCanLoad();\n validateTokens(tokens);\n const previousState = this.#stateMachine.state;\n this.#stateMachine.transition(\"load\");\n this.#scheduler.cancel();\n this.#setTokens(tokens);\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n if (previousState !== \"IDLE\") {\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"IDLE\" });\n }\n }\n\n #assertCanLoad(): void {\n if (this.#stateMachine.state === \"PLAYING\" || this.#stateMachine.state === \"ERROR\") {\n throw new InvalidInputError(`Cannot load data while engine is ${this.#stateMachine.state}.`);\n }\n }\n\n #setTokens(tokens: Token<T>[]): void {\n this.#tokens = tokens.map((token, index) => ({\n value: token.value,\n index,\n ovpIndex: token.ovpIndex,\n delayMultiplier: token.delayMultiplier,\n }));\n }\n\n // ────────── Error handling ──────────\n\n #enterFatalError(error: Error): void {\n const previousState = this.#stateMachine.state;\n try {\n this.#stateMachine.transition(\"error\");\n } catch {\n this.#reportError(error);\n return;\n }\n this.#scheduler.cancel();\n this.#deadline = null;\n this.#remainingDelay = null;\n this.#reportError(error);\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"ERROR\" });\n }\n\n #reportError(error: Error): void {\n this.#emitter.emit(\"error\", { error });\n }\n\n #toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n\n // ────────── Destroyed guard ──────────\n\n #assertNotDestroyed(): void {\n if (this.#destroyed) {\n throw new EngineDestroyedError();\n }\n }\n\n // ────────── Tick loop ──────────\n\n readonly #advance = (): void => {\n this.#deadline = null;\n this.#remainingDelay = null;\n if (this.#stateMachine.state !== \"PLAYING\") {\n return;\n }\n\n if (this.#currentIndex >= this.#tokens.length - 1) {\n this.#completePlayback();\n return;\n }\n\n this.#currentIndex++;\n this.#presentCurrent();\n };\n\n #presentCurrent(): void {\n const item = this.#tokens[this.#currentIndex];\n this.#hasPresentedCurrent = true;\n\n this.#emitter.emit(\"itemChange\", {\n item,\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"playback\",\n });\n if (this.#stateMachine.state !== \"PLAYING\") {\n return;\n }\n\n this.#scheduleAdvance(this.#msPerItem * item.delayMultiplier);\n }\n\n #scheduleAdvance(delay: number): void {\n this.#remainingDelay = null;\n this.#deadline = this.#timeDriver.now() + delay;\n try {\n this.#scheduler.schedule(this.#advance, delay);\n } catch (error) {\n this.#enterFatalError(this.#toError(error));\n }\n }\n\n #completePlayback(): void {\n this.#scheduler.cancel();\n const previousState = this.#stateMachine.state;\n this.#stateMachine.transition(\"complete\");\n const item = this.#tokens[this.#currentIndex];\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"COMPLETED\" });\n this.#emitter.emit(\"complete\", { item, totalItems: this.#tokens.length });\n }\n\n // ────────── Playback Control ──────────\n\n /**\n * Starts or resumes playback.\n * From STOPPED/COMPLETED: resets index to 0 and starts from beginning.\n * From PAUSED: resumes from current position.\n */\n public play(): void {\n this.#assertNotDestroyed();\n this.#startPlayback();\n }\n\n #startPlayback(): void {\n // Validate we have tokens\n if (this.#tokens.length === 0) {\n this.#reportError(new InvalidInputError(\"Cannot play: no tokens loaded.\"));\n return;\n }\n\n const previousState = this.#stateMachine.state;\n\n // Reset index if coming from STOPPED or COMPLETED\n if (previousState === \"STOPPED\" || previousState === \"COMPLETED\") {\n this.#currentIndex = 0;\n }\n\n try {\n this.#stateMachine.transition(\"play\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PLAYING\",\n });\n\n if (previousState === \"PAUSED\" && this.#hasPresentedCurrent) {\n const item = this.#tokens[this.#currentIndex];\n this.#scheduleAdvance(this.#remainingDelay ?? this.#msPerItem * item.delayMultiplier);\n return;\n }\n\n this.#hasPresentedCurrent = false;\n this.#presentCurrent();\n }\n\n /**\n * Pauses playback, preserving the current index.\n */\n public pause(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"pause\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#remainingDelay =\n this.#deadline === null ? null : Math.max(0, this.#deadline - this.#timeDriver.now());\n this.#scheduler.cancel();\n this.#deadline = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PAUSED\",\n });\n }\n\n /**\n * Stops playback and resets the index to 0.\n */\n public stop(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"stop\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#scheduler.cancel();\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"STOPPED\",\n });\n }\n\n /**\n * Jumps to a specific token index. Transitions to PAUSED state.\n * Only available from PAUSED, STOPPED, or COMPLETED states.\n */\n public seek(index: number): void {\n this.#assertNotDestroyed();\n\n if (index < 0 || index >= this.#tokens.length) {\n this.#reportError(new IndexOutOfBoundsError(index, this.#tokens.length));\n return;\n }\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"seek\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#currentIndex = index;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.#msPerItem * this.#tokens[index].delayMultiplier;\n\n if (previousState !== \"PAUSED\") {\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PAUSED\",\n });\n }\n\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[index],\n index,\n progress: this.progress,\n reason: \"seek\",\n });\n }\n\n /**\n * Advances to the next token. Only available from PAUSED state.\n */\n public next(): void {\n this.#assertNotDestroyed();\n\n if (this.#stateMachine.state !== \"PAUSED\") {\n this.#emitter.emit(\"error\", {\n error: new InvalidInputError(\"next() is only available in PAUSED state.\"),\n });\n return;\n }\n\n if (this.#currentIndex < this.#tokens.length - 1) {\n this.#currentIndex++;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.#msPerItem * this.#tokens[this.#currentIndex].delayMultiplier;\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[this.#currentIndex],\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"next\",\n });\n }\n }\n\n /**\n * Retreats to the previous token. Only available from PAUSED state.\n */\n public previous(): void {\n this.#assertNotDestroyed();\n\n if (this.#stateMachine.state !== \"PAUSED\") {\n this.#emitter.emit(\"error\", {\n error: new InvalidInputError(\"previous() is only available in PAUSED state.\"),\n });\n return;\n }\n\n if (this.#currentIndex > 0) {\n this.#currentIndex--;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.#msPerItem * this.#tokens[this.#currentIndex].delayMultiplier;\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[this.#currentIndex],\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"previous\",\n });\n }\n }\n\n /**\n * Resets engine to IDLE state, clearing all data.\n * Only valid from ERROR state.\n */\n public reset(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"reset\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#scheduler.cancel();\n this.#tokens = [];\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"IDLE\",\n });\n }\n\n /**\n * Clears all timers, removes listeners, and disposes the engine.\n * Required for SPA component unmount cleanup.\n */\n public destroy(): void {\n if (this.#destroyed) {\n return; // idempotent\n }\n this.#scheduler.cancel();\n this.#deadline = null;\n this.#remainingDelay = null;\n this.#emitter.removeAllListeners();\n this.#destroyed = true;\n }\n\n // ────────── Speed Control ──────────\n\n /**\n * Updates playback speed via WPM (live, mid-playback).\n */\n public setSpeed(wpm: number): void {\n this.#assertNotDestroyed();\n validateWpm(wpm);\n this.#msPerItem = 60_000 / wpm;\n }\n\n /**\n * Directly sets ms-per-item interval (live, mid-playback).\n */\n public setMsPerItem(ms: number): void {\n this.#assertNotDestroyed();\n validateMsPerItem(ms);\n this.#msPerItem = ms;\n }\n\n // ────────── State & Snapshot Getters ──────────\n\n /** Current engine state. */\n public get state(): RSVPState {\n return this.#stateMachine.state;\n }\n\n /** Current WPM computed from msPerItem. */\n public get wpm(): number {\n return 60_000 / this.#msPerItem;\n }\n\n /** Current ms-per-item interval. */\n public get msPerItem(): number {\n return this.#msPerItem;\n }\n\n /** Current token index (0-based). */\n public get currentIndex(): number {\n return this.#currentIndex;\n }\n\n /** Current token/item being displayed, or null if none. */\n public get currentItem(): RSVPItem<T> | null {\n return this.#tokens[this.#currentIndex] ?? null;\n }\n\n /** Playback progress (0–1). */\n public get progress(): number {\n if (this.#tokens.length === 0) {\n return 0;\n }\n return this.#hasPresentedCurrent ? (this.#currentIndex + 1) / this.#tokens.length : 0;\n }\n\n /** Total number of tokens/items. */\n public get totalItems(): number {\n return this.#tokens.length;\n }\n\n // ────────── Event System ──────────\n\n /**\n * Subscribes to an engine event. Returns an unsubscribe function.\n */\n public on<K extends RSVPEventType>(\n event: K,\n callback: EventCallback<RSVPEventMap<T>[K]>,\n ): UnsubscribeFn {\n this.#assertNotDestroyed();\n return this.#emitter.on(event, callback);\n }\n\n // ────────── Snapshot ──────────\n\n /**\n * Returns a frozen, readonly snapshot of the full engine state.\n */\n public snapshot(): RSVPSnapshot<T> {\n this.#assertNotDestroyed();\n return Object.freeze({\n state: this.state,\n currentIndex: this.#currentIndex,\n currentItem: this.currentItem,\n progress: this.progress,\n totalItems: this.totalItems,\n wpm: this.wpm,\n msPerItem: this.#msPerItem,\n });\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/engine/config.ts","../src/errors.ts","../src/events/event-emitter.ts","../src/scheduler/system-time-driver.ts","../src/scheduler/drift-corrected-scheduler.ts","../src/state/state-machine.ts","../src/tokenizer/ovp.ts","../src/tokenizer/default-tokenizer.ts","../src/engine/validation.ts","../src/engine/rsvp-engine.ts"],"names":["MIN_WPM","MAX_WPM","MIN_MS_PER_ITEM","MAX_MS_PER_ITEM","DEFAULT_WPM","InvalidTransitionError","from","action","EngineDestroyedError","InvalidInputError","message","IndexOutOfBoundsError","index","totalItems","EventEmitter","#listenerRegistry","event","callback","eventListeners","payload","listenersSnapshot","listener","err","error","SystemTimeDriver","runtimeGlobal","ms","handle","DriftCorrectedScheduler","#timeDriver","#timeoutHandle","#expectedTime","timeDriver","task","delayMs","now","actualDelay","drift","#clearTimer","StateMachine","#state","#transitions","nextState","DefaultOVPStrategy","text","graphemes","segmentGraphemes","content","segment","isPunctuation","target","Segmenter","previous","value","DefaultTokenizer","#sentenceDelay","#clauseDelay","#dashDelay","#nestedTokenize","#ovpStrategy","options","#validateOptions","input","#tokenizeArray","#tokenizeText","#calculateDelay","items","item","trimmed","#segmentWords","word","name","segmenter","tokens","prefix","atBoundary","part","validateTokens","token","validateWpm","wpm","validateMsPerItem","RSVPEngine","#stateMachine","#emitter","#scheduler","#tokenizer","#tokens","#currentIndex","#hasPresentedCurrent","#deadline","#remainingDelay","#speed","#destroyed","data","#assertNotDestroyed","#assertCanLoad","#enterFatalError","#toError","previousState","#setTokens","#reportError","#advance","#completePlayback","#presentCurrent","#scheduleAdvance","delay","#startPlayback"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { SchedulerStrategy, TimeDriver } from \"../scheduler/types\";\nimport type { TokenizerStrategy } from \"../tokenizer/types\";\n\n/**\n * Configuration options for the RSVP Engine.\n */\nexport interface RSVPEngineOptions<T = string> {\n /** Raw text string, single generic item, or generic item array. */\n readonly data?: T | T[];\n /** Target WPM, preserved exactly unless msPerItem is supplied. Defaults to 300. */\n readonly wpm?: number;\n /** Direct ms-per-item override. Takes precedence over `wpm`. */\n readonly msPerItem?: number;\n /** Custom tokenizer strategy. Defaults to `DefaultTokenizer`. */\n readonly tokenizer?: TokenizerStrategy<T>;\n /** Custom scheduler strategy. Defaults to `DriftCorrectedScheduler`. */\n readonly scheduler?: SchedulerStrategy;\n /** Clock used to preserve remaining display time across pause/resume. */\n readonly timeDriver?: TimeDriver;\n}\n\n/** Minimum allowed playback speed in Words Per Minute. */\nexport const MIN_WPM = 1;\n/** Maximum allowed playback speed in Words Per Minute. */\nexport const MAX_WPM = 6_000;\n/** Minimum allowed display interval per item in milliseconds. */\nexport const MIN_MS_PER_ITEM = 60_000 / MAX_WPM;\n/** Maximum allowed display interval per item in milliseconds. */\nexport const MAX_MS_PER_ITEM = 60_000 / MIN_WPM;\n/** Default playback speed in Words Per Minute. */\nexport const DEFAULT_WPM = 300;\n","/**\n * Thrown when an invalid state transition is attempted.\n *\n */\nexport class InvalidTransitionError extends Error {\n public readonly from: string;\n public readonly action: string;\n\n constructor(from: string, action: string) {\n super(`Invalid transition: cannot perform '${action}' from '${from}' state.`);\n this.name = \"InvalidTransitionError\";\n this.from = from;\n this.action = action;\n }\n}\n\n/**\n * Thrown when a method is called on a destroyed engine instance.\n */\nexport class EngineDestroyedError extends Error {\n constructor() {\n super(\"Cannot interact with a destroyed engine instance.\");\n this.name = \"EngineDestroyedError\";\n }\n}\n\n/**\n * Thrown when invalid input is provided to the engine (empty text, null data, etc.).\n */\nexport class InvalidInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidInputError\";\n }\n}\n\n/**\n * Thrown when a seek index is out of bounds.\n */\nexport class IndexOutOfBoundsError extends Error {\n public readonly index: number;\n public readonly totalItems: number;\n\n constructor(index: number, totalItems: number) {\n super(`Index ${index} is out of bounds (0..${totalItems - 1}).`);\n this.name = \"IndexOutOfBoundsError\";\n this.index = index;\n this.totalItems = totalItems;\n }\n}\n","import type { EventCallback, RSVPEventMap, RSVPEventType, UnsubscribeFn } from \"./types\";\n\ntype ListenerRegistry<T> = {\n [K in RSVPEventType]: Set<EventCallback<RSVPEventMap<T>[K]>>;\n};\n\nexport class EventEmitter<T = string> {\n readonly #listenerRegistry: ListenerRegistry<T> = {\n stateChange: new Set(),\n itemChange: new Set(),\n complete: new Set(),\n error: new Set(),\n };\n\n public on<K extends RSVPEventType>(\n event: K,\n callback: EventCallback<RSVPEventMap<T>[K]>,\n ): UnsubscribeFn {\n const eventListeners = this.#listenerRegistry[event];\n eventListeners.add(callback);\n\n return () => {\n eventListeners.delete(callback);\n };\n }\n\n public emit<K extends RSVPEventType>(event: K, payload: RSVPEventMap<T>[K]): void {\n const eventListeners = this.#listenerRegistry[event];\n\n // Copy to array to handle safe iteration if unsubscribed during emit\n const listenersSnapshot = Array.from(eventListeners);\n\n for (const listener of listenersSnapshot) {\n try {\n listener(payload);\n } catch (err) {\n if (event !== \"error\") {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit(\"error\", { error });\n }\n }\n }\n }\n\n public removeAllListeners(): void {\n for (const eventListeners of Object.values(this.#listenerRegistry)) {\n eventListeners.clear();\n }\n }\n}\n","import type { TimeDriver } from \"./types\";\nimport type { RuntimeGlobal } from \"../runtimeGlobal\";\n\n/**\n * Default implementation of `TimeDriver` utilizing platform globals\n * (`performance.now` / `Date.now` and `setTimeout` / `clearTimeout`).\n */\nexport class SystemTimeDriver implements TimeDriver {\n public now(): number {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.performance?.now === \"function\") {\n return runtimeGlobal.performance.now();\n }\n return Date.now();\n }\n\n public setTimeout(callback: () => void, ms: number): unknown {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.setTimeout !== \"function\") {\n throw new TypeError(\"The runtime does not provide setTimeout.\");\n }\n return runtimeGlobal.setTimeout(callback, ms);\n }\n\n public clearTimeout(handle: unknown): void {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.clearTimeout !== \"function\") {\n throw new TypeError(\"The runtime does not provide clearTimeout.\");\n }\n runtimeGlobal.clearTimeout(handle);\n }\n}\n","import type { SchedulerStrategy, TimeDriver } from \"./types\";\n\nimport { SystemTimeDriver } from \"./system-time-driver\";\n\n/**\n * High-precision, drift-corrected task scheduler.\n *\n * Implements the {@link SchedulerStrategy} interface by tracking an ideal target timeline\n * (`#expectedTime`) across consecutive task executions. Any latency or jitter introduced\n * by the JavaScript event loop (timer drift) is calculated and subtracted from subsequent\n * intervals, ensuring stable WPM presentation without cumulative time slippage.\n *\n * @example\n * ```ts\n * const scheduler = new DriftCorrectedScheduler();\n *\n * // Schedule a task after 200ms\n * scheduler.schedule(() => {\n * console.log(\"Tick!\");\n * }, 200);\n *\n * // Cancel any pending task\n * scheduler.cancel();\n * ```\n */\nexport class DriftCorrectedScheduler implements SchedulerStrategy {\n readonly #timeDriver: TimeDriver;\n #timeoutHandle: unknown = null;\n #expectedTime: number | null = null;\n\n /**\n * Creates a new instance of `DriftCorrectedScheduler`.\n *\n * @param timeDriver - The underlying time and timer provider. Defaults to {@link SystemTimeDriver}.\n */\n constructor(timeDriver: TimeDriver = new SystemTimeDriver()) {\n this.#timeDriver = timeDriver;\n }\n\n /**\n * Schedules a task to execute after the given delay, compensating for accumulated drift.\n *\n * If a previous task is currently pending, it is automatically cancelled before scheduling\n * the new one. When part of an active session, the actual timeout duration is adjusted\n * by subtracting any detected execution delay (`drift = now - expectedTime`).\n *\n * @param task - The callback function to execute.\n * @param delayMs - The target delay in milliseconds.\n */\n public schedule(task: () => void, delayMs: number): void {\n if (!Number.isFinite(delayMs) || delayMs <= 0) {\n throw new RangeError(\"delayMs must be a positive finite number.\");\n }\n\n if (this.#timeoutHandle !== null) {\n this.cancel();\n }\n\n const now = this.#timeDriver.now();\n let actualDelay = delayMs;\n\n if (this.#expectedTime === null) {\n this.#expectedTime = now + delayMs;\n } else {\n const drift = now - this.#expectedTime;\n if (drift >= delayMs) {\n // Large catch-up bursts make RSVP items unreadable; start a fresh timeline instead.\n this.#expectedTime = now + delayMs;\n } else {\n this.#expectedTime += delayMs;\n actualDelay = Math.max(delayMs / 2, delayMs - drift);\n }\n }\n\n this.#timeoutHandle = this.#timeDriver.setTimeout(() => {\n this.#timeoutHandle = null;\n task();\n }, actualDelay);\n }\n\n /**\n * Cancels any pending scheduled task and resets the session timeline.\n *\n * Calling this method halts the drift compensation timeline so that the next\n * call to {@link schedule} starts a fresh session without previous drift history.\n */\n public cancel(): void {\n this.#clearTimer();\n this.#expectedTime = null;\n }\n\n #clearTimer(): void {\n if (this.#timeoutHandle !== null) {\n this.#timeDriver.clearTimeout(this.#timeoutHandle);\n this.#timeoutHandle = null;\n }\n }\n}\n","import type { RSVPState, StateMachineAction } from \"./types\";\n\nimport { InvalidTransitionError } from \"../errors\";\n\nexport class StateMachine {\n #state: RSVPState = \"IDLE\";\n\n readonly #transitions: Record<RSVPState, Partial<Record<StateMachineAction, RSVPState>>> = {\n IDLE: {\n load: \"IDLE\",\n play: \"PLAYING\",\n error: \"ERROR\",\n },\n PLAYING: {\n pause: \"PAUSED\",\n stop: \"STOPPED\",\n complete: \"COMPLETED\",\n error: \"ERROR\",\n },\n PAUSED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n stop: \"STOPPED\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n STOPPED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n COMPLETED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n ERROR: {\n reset: \"IDLE\",\n },\n };\n\n public get state(): RSVPState {\n return this.#state;\n }\n\n public transition(action: StateMachineAction): RSVPState {\n const allowedTransitions = this.#transitions[this.#state];\n const nextState = allowedTransitions[action];\n\n if (!nextState) {\n throw new InvalidTransitionError(this.#state, action);\n }\n\n this.#state = nextState;\n return this.#state;\n }\n}\n","import type { OVPStrategy } from \"./types\";\n\n/**\n * Default grapheme-aware Optimal Viewing Position strategy.\n */\nexport class DefaultOVPStrategy implements OVPStrategy {\n /**\n * Calculates the Optimal Viewing Position for text.\n * @returns The zero-based UTF-16 offset of the OVP grapheme.\n */\n public calculate(text: string): number {\n if (!text) {\n return 0;\n }\n\n const graphemes = segmentGraphemes(text);\n const content = graphemes.filter(({ segment }) => !isPunctuation(segment));\n\n if (content.length === 0) {\n return graphemes[0].index;\n }\n\n const target = content.length <= 3 ? 0 : Math.floor(content.length * 0.25);\n return content[target].index;\n }\n}\n\ninterface Grapheme {\n readonly index: number;\n readonly segment: string;\n}\n\nfunction segmentGraphemes(text: string): Grapheme[] {\n const Segmenter = globalThis.Intl?.Segmenter;\n if (typeof Segmenter === \"function\") {\n return Array.from(new Segmenter(undefined, { granularity: \"grapheme\" }).segment(text));\n }\n\n const graphemes: Grapheme[] = [];\n let index = 0;\n for (const segment of text) {\n const previous = graphemes.at(-1);\n if (\n previous &&\n (/^[\\p{M}\\uFE0F]$/u.test(segment) ||\n segment === \"\\u200D\" ||\n previous.segment.endsWith(\"\\u200D\"))\n ) {\n graphemes[graphemes.length - 1] = {\n index: previous.index,\n segment: previous.segment + segment,\n };\n } else {\n graphemes.push({ index, segment });\n }\n index += segment.length;\n }\n return graphemes;\n}\n\nfunction isPunctuation(value: string): boolean {\n return /^[\\p{P}\\p{Z}]+$/u.test(value);\n}\n","import type { OVPStrategy, Token, TokenizerStrategy } from \"./types\";\n\nimport { InvalidInputError } from \"../errors\";\n\nimport { DefaultOVPStrategy } from \"./ovp\";\n\n/**\n * Options for configuring `DefaultTokenizer`.\n */\nexport interface DefaultTokenizerOptions {\n /** Multiplier for sentence-ending punctuation (`.`, `!`, `?`). Defaults to `2.0`. */\n readonly sentenceDelay?: number;\n /** Multiplier for clause-ending punctuation (`,`, `;`, `:`). Defaults to `1.5`. */\n readonly clauseDelay?: number;\n /** Multiplier for dashes (`—`, `–`). Defaults to `1.3`. */\n readonly dashDelay?: number;\n /** Whether array elements and nested inputs should be recursively tokenized. Defaults to `false`. */\n readonly nestedTokenize?: boolean;\n /** Strategy used to calculate viewing positions for string tokens. */\n readonly ovpStrategy?: OVPStrategy;\n}\n\n/**\n * Default configurable tokenizer for text, items, and array inputs.\n * Splits text on whitespace and assigns delay multipliers based on punctuation.\n * Wraps custom generic items or arrays into presentable tokens.\n * @typeParam T - The type of items being presented (defaults to `string`).\n */\nexport class DefaultTokenizer<T> implements TokenizerStrategy<T> {\n readonly #sentenceDelay: number;\n readonly #clauseDelay: number;\n readonly #dashDelay: number;\n readonly #nestedTokenize: boolean;\n readonly #ovpStrategy: OVPStrategy;\n\n constructor(options: DefaultTokenizerOptions = {}) {\n this.#sentenceDelay = options.sentenceDelay ?? 2.0;\n this.#clauseDelay = options.clauseDelay ?? 1.5;\n this.#dashDelay = options.dashDelay ?? 1.3;\n this.#nestedTokenize = options.nestedTokenize ?? false;\n this.#ovpStrategy = options.ovpStrategy ?? new DefaultOVPStrategy();\n this.#validateOptions();\n }\n\n public tokenize(input: T | T[]): Token<T>[] {\n if (input === null || input === undefined) {\n return [];\n }\n\n if (Array.isArray(input)) {\n return this.#tokenizeArray(input);\n }\n\n if (typeof input === \"string\") {\n return this.#tokenizeText(input) as Token<T>[];\n }\n\n return [\n {\n value: input,\n ovpIndex: 0,\n delayMultiplier: this.#calculateDelay(input),\n },\n ];\n }\n\n #tokenizeArray(items: T[]): Token<T>[] {\n if (this.#nestedTokenize) {\n return items.flatMap((item) => this.tokenize(item));\n }\n\n return items.map((item) => ({\n value: item,\n ovpIndex: typeof item === \"string\" ? this.#ovpStrategy.calculate(item) : 0,\n delayMultiplier: this.#calculateDelay(item),\n }));\n }\n\n #tokenizeText(text: string): Token<string>[] {\n const trimmed = text.trim();\n if (!trimmed) {\n return [];\n }\n\n const words = this.#segmentWords(trimmed);\n return words.map((word) => ({\n value: word,\n ovpIndex: this.#ovpStrategy.calculate(word),\n delayMultiplier: this.#calculateDelay(word),\n }));\n }\n\n #calculateDelay(item: unknown): number {\n if (typeof item === \"string\") {\n if (/[.!?。!?…]+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#sentenceDelay;\n }\n if (/[,;:,、;:]+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#clauseDelay;\n }\n if (/\\p{Pd}+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#dashDelay;\n }\n return 1.0;\n }\n\n return 1.0;\n }\n\n #validateOptions(): void {\n const options = [\n [\"sentenceDelay\", this.#sentenceDelay],\n [\"clauseDelay\", this.#clauseDelay],\n [\"dashDelay\", this.#dashDelay],\n ] as const;\n for (const [name, value] of options) {\n if (!Number.isFinite(value) || value <= 0) {\n throw new InvalidInputError(`${name} must be a positive finite number.`);\n }\n }\n }\n\n #segmentWords(text: string): string[] {\n const Segmenter = globalThis.Intl?.Segmenter;\n if (typeof Segmenter !== \"function\") {\n return text.split(/\\s+/);\n }\n\n const segmenter = new Segmenter(undefined, { granularity: \"word\" });\n const tokens: string[] = [];\n let prefix = \"\";\n let atBoundary = true;\n for (const part of segmenter.segment(text)) {\n if (/^\\s+$/u.test(part.segment)) {\n if (prefix) {\n tokens.push(prefix);\n }\n prefix = \"\";\n atBoundary = true;\n } else if (part.isWordLike || !isPunctuation(part.segment)) {\n tokens.push(prefix + part.segment);\n prefix = \"\";\n atBoundary = false;\n } else if (atBoundary || prefix || /^[\\p{Ps}\\p{Pi}]+$/u.test(part.segment)) {\n prefix += part.segment;\n } else {\n tokens[tokens.length - 1] += part.segment;\n }\n }\n if (prefix) {\n tokens.push(prefix);\n }\n return tokens;\n }\n}\n\nfunction isPunctuation(value: string): boolean {\n return /^\\p{P}+$/u.test(value);\n}\n","import type { Token } from \"../tokenizer/types\";\n\nimport { InvalidInputError } from \"../errors\";\n\nimport { MAX_MS_PER_ITEM, MAX_WPM, MIN_MS_PER_ITEM, MIN_WPM } from \"./config\";\n\nexport function validateTokens<T>(tokens: Token<T>[]): void {\n if (!Array.isArray(tokens)) {\n throw new InvalidInputError(\"tokens must be an array.\");\n }\n\n for (const [index, token] of tokens.entries()) {\n if (typeof token !== \"object\" || token === null) {\n throw new InvalidInputError(`Token ${index} must be an object.`);\n }\n if (!Number.isInteger(token.ovpIndex) || token.ovpIndex < 0) {\n throw new InvalidInputError(`Token ${index} has an invalid ovpIndex.`);\n }\n if (typeof token.value === \"string\" && token.ovpIndex > token.value.length) {\n throw new InvalidInputError(`Token ${index} has an out-of-range ovpIndex.`);\n }\n if (!Number.isFinite(token.delayMultiplier) || token.delayMultiplier <= 0) {\n throw new InvalidInputError(`Token ${index} has an invalid delayMultiplier.`);\n }\n }\n}\n\nexport function validateWpm(wpm: number): void {\n if (!Number.isFinite(wpm) || wpm < MIN_WPM || wpm > MAX_WPM) {\n throw new InvalidInputError(`wpm must be a finite number between ${MIN_WPM} and ${MAX_WPM}.`);\n }\n}\n\nexport function validateMsPerItem(ms: number): void {\n if (!Number.isFinite(ms) || ms < MIN_MS_PER_ITEM || ms > MAX_MS_PER_ITEM) {\n throw new InvalidInputError(\n `msPerItem must be a finite number between ${MIN_MS_PER_ITEM} and ${MAX_MS_PER_ITEM}.`,\n );\n }\n}\n","import type { RSVPEngineOptions } from \"./config\";\nimport type { RSVPItem, RSVPSnapshot } from \"./types\";\nimport type { EventCallback, RSVPEventMap, RSVPEventType, UnsubscribeFn } from \"../events/types\";\nimport type { SchedulerStrategy, TimeDriver } from \"../scheduler/types\";\nimport type { RSVPState } from \"../state/types\";\nimport type { Token, TokenizerStrategy } from \"../tokenizer/types\";\n\nimport { EngineDestroyedError, IndexOutOfBoundsError, InvalidInputError } from \"../errors\";\nimport { EventEmitter } from \"../events/event-emitter\";\nimport { DriftCorrectedScheduler } from \"../scheduler/drift-corrected-scheduler\";\nimport { SystemTimeDriver } from \"../scheduler/system-time-driver\";\nimport { StateMachine } from \"../state/state-machine\";\nimport { DefaultTokenizer } from \"../tokenizer/default-tokenizer\";\n\nimport { DEFAULT_WPM } from \"./config\";\nimport { validateMsPerItem, validateTokens, validateWpm } from \"./validation\";\n\ninterface SpeedSetting {\n readonly unit: \"wpm\" | \"msPerItem\";\n readonly value: number;\n}\n\n/**\n * Headless RSVP Engine — orchestrates state machine, scheduler, tokenizer, and event emitter.\n * @typeParam T - The type of items being presented (defaults to `string`).\n */\nexport class RSVPEngine<T = string> {\n readonly #stateMachine: StateMachine;\n readonly #emitter: EventEmitter<T>;\n readonly #scheduler: SchedulerStrategy;\n readonly #timeDriver: TimeDriver;\n readonly #tokenizer: TokenizerStrategy<T>;\n #tokens: RSVPItem<T>[] = [];\n #currentIndex = 0;\n #hasPresentedCurrent = false;\n #deadline: number | null = null;\n #remainingDelay: number | null = null;\n #speed: SpeedSetting = { unit: \"wpm\", value: DEFAULT_WPM };\n #destroyed = false;\n\n constructor(options: RSVPEngineOptions<T> = {}) {\n this.#stateMachine = new StateMachine();\n this.#emitter = new EventEmitter<T>();\n\n this.#timeDriver = options.timeDriver ?? new SystemTimeDriver();\n this.#scheduler = options.scheduler ?? new DriftCorrectedScheduler(this.#timeDriver);\n this.#tokenizer = options.tokenizer ?? new DefaultTokenizer<T>();\n\n // msPerItem takes precedence over wpm\n if (options.msPerItem !== undefined) {\n this.setMsPerItem(options.msPerItem);\n } else if (options.wpm !== undefined) {\n this.setSpeed(options.wpm);\n }\n\n // Constructor failures must remain observable because listeners cannot be attached yet.\n if (options.data !== undefined) {\n this.load(options.data);\n }\n }\n\n // ────────── Tokenization & Loading ──────────\n\n /**\n * Tokenizes and loads raw data into the engine using the configured tokenizer.\n */\n public load(data: T | T[]): void {\n this.#assertNotDestroyed();\n this.#assertCanLoad();\n try {\n this.loadTokens(this.#tokenizer.tokenize(data));\n } catch (error) {\n if (error instanceof InvalidInputError) {\n throw error;\n }\n this.#enterFatalError(this.#toError(error));\n throw error;\n }\n }\n\n /**\n * Directly loads pre-tokenized items into the engine.\n * Useful when tokenization is performed externally (e.g. AI or asynchronous tokenizers).\n */\n public loadTokens(tokens: Token<T>[]): void {\n this.#assertNotDestroyed();\n this.#assertCanLoad();\n validateTokens(tokens);\n const previousState = this.#stateMachine.state;\n this.#stateMachine.transition(\"load\");\n this.#scheduler.cancel();\n this.#setTokens(tokens);\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n if (previousState !== \"IDLE\") {\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"IDLE\" });\n }\n }\n\n #assertCanLoad(): void {\n if (this.#stateMachine.state === \"PLAYING\" || this.#stateMachine.state === \"ERROR\") {\n throw new InvalidInputError(`Cannot load data while engine is ${this.#stateMachine.state}.`);\n }\n }\n\n #setTokens(tokens: Token<T>[]): void {\n this.#tokens = tokens.map((token, index) => ({\n value: token.value,\n index,\n ovpIndex: token.ovpIndex,\n delayMultiplier: token.delayMultiplier,\n }));\n }\n\n // ────────── Error handling ──────────\n\n #enterFatalError(error: Error): void {\n const previousState = this.#stateMachine.state;\n try {\n this.#stateMachine.transition(\"error\");\n } catch {\n this.#reportError(error);\n return;\n }\n this.#scheduler.cancel();\n this.#deadline = null;\n this.#remainingDelay = null;\n this.#reportError(error);\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"ERROR\" });\n }\n\n #reportError(error: Error): void {\n this.#emitter.emit(\"error\", { error });\n }\n\n #toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n\n // ────────── Destroyed guard ──────────\n\n #assertNotDestroyed(): void {\n if (this.#destroyed) {\n throw new EngineDestroyedError();\n }\n }\n\n // ────────── Tick loop ──────────\n\n readonly #advance = (): void => {\n this.#deadline = null;\n this.#remainingDelay = null;\n if (this.#stateMachine.state !== \"PLAYING\") {\n return;\n }\n\n if (this.#currentIndex >= this.#tokens.length - 1) {\n this.#completePlayback();\n return;\n }\n\n this.#currentIndex++;\n this.#presentCurrent();\n };\n\n #presentCurrent(): void {\n const item = this.#tokens[this.#currentIndex];\n this.#hasPresentedCurrent = true;\n\n this.#emitter.emit(\"itemChange\", {\n item,\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"playback\",\n });\n if (this.#stateMachine.state !== \"PLAYING\") {\n return;\n }\n\n this.#scheduleAdvance(this.msPerItem * item.delayMultiplier);\n }\n\n #scheduleAdvance(delay: number): void {\n this.#remainingDelay = null;\n this.#deadline = this.#timeDriver.now() + delay;\n try {\n this.#scheduler.schedule(this.#advance, delay);\n } catch (error) {\n this.#enterFatalError(this.#toError(error));\n }\n }\n\n #completePlayback(): void {\n this.#scheduler.cancel();\n const previousState = this.#stateMachine.state;\n this.#stateMachine.transition(\"complete\");\n const item = this.#tokens[this.#currentIndex];\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"COMPLETED\" });\n this.#emitter.emit(\"complete\", { item, totalItems: this.#tokens.length });\n }\n\n // ────────── Playback Control ──────────\n\n /**\n * Starts or resumes playback.\n * From STOPPED/COMPLETED: resets index to 0 and starts from beginning.\n * From PAUSED: resumes from current position.\n */\n public play(): void {\n this.#assertNotDestroyed();\n this.#startPlayback();\n }\n\n #startPlayback(): void {\n // Validate we have tokens\n if (this.#tokens.length === 0) {\n this.#reportError(new InvalidInputError(\"Cannot play: no tokens loaded.\"));\n return;\n }\n\n const previousState = this.#stateMachine.state;\n\n // Reset index if coming from STOPPED or COMPLETED\n if (previousState === \"STOPPED\" || previousState === \"COMPLETED\") {\n this.#currentIndex = 0;\n }\n\n try {\n this.#stateMachine.transition(\"play\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PLAYING\",\n });\n\n if (previousState === \"PAUSED\" && this.#hasPresentedCurrent) {\n const item = this.#tokens[this.#currentIndex];\n this.#scheduleAdvance(this.#remainingDelay ?? this.msPerItem * item.delayMultiplier);\n return;\n }\n\n this.#hasPresentedCurrent = false;\n this.#presentCurrent();\n }\n\n /**\n * Pauses playback, preserving the current index.\n */\n public pause(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"pause\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#remainingDelay =\n this.#deadline === null ? null : Math.max(0, this.#deadline - this.#timeDriver.now());\n this.#scheduler.cancel();\n this.#deadline = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PAUSED\",\n });\n }\n\n /**\n * Stops playback and resets the index to 0.\n */\n public stop(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"stop\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#scheduler.cancel();\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"STOPPED\",\n });\n }\n\n /**\n * Jumps to a specific token index. Transitions to PAUSED state.\n * Only available from PAUSED, STOPPED, or COMPLETED states.\n */\n public seek(index: number): void {\n this.#assertNotDestroyed();\n\n if (index < 0 || index >= this.#tokens.length) {\n this.#reportError(new IndexOutOfBoundsError(index, this.#tokens.length));\n return;\n }\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"seek\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#currentIndex = index;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.msPerItem * this.#tokens[index].delayMultiplier;\n\n if (previousState !== \"PAUSED\") {\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PAUSED\",\n });\n }\n\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[index],\n index,\n progress: this.progress,\n reason: \"seek\",\n });\n }\n\n /**\n * Advances to the next token. Only available from PAUSED state.\n */\n public next(): void {\n this.#assertNotDestroyed();\n\n if (this.#stateMachine.state !== \"PAUSED\") {\n this.#emitter.emit(\"error\", {\n error: new InvalidInputError(\"next() is only available in PAUSED state.\"),\n });\n return;\n }\n\n if (this.#currentIndex < this.#tokens.length - 1) {\n this.#currentIndex++;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.msPerItem * this.#tokens[this.#currentIndex].delayMultiplier;\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[this.#currentIndex],\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"next\",\n });\n }\n }\n\n /**\n * Retreats to the previous token. Only available from PAUSED state.\n */\n public previous(): void {\n this.#assertNotDestroyed();\n\n if (this.#stateMachine.state !== \"PAUSED\") {\n this.#emitter.emit(\"error\", {\n error: new InvalidInputError(\"previous() is only available in PAUSED state.\"),\n });\n return;\n }\n\n if (this.#currentIndex > 0) {\n this.#currentIndex--;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.msPerItem * this.#tokens[this.#currentIndex].delayMultiplier;\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[this.#currentIndex],\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"previous\",\n });\n }\n }\n\n /**\n * Resets engine to IDLE state, clearing all data.\n * Only valid from ERROR state.\n */\n public reset(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"reset\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#scheduler.cancel();\n this.#tokens = [];\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"IDLE\",\n });\n }\n\n /**\n * Clears all timers, removes listeners, and disposes the engine.\n * Required for SPA component unmount cleanup.\n */\n public destroy(): void {\n if (this.#destroyed) {\n return; // idempotent\n }\n this.#scheduler.cancel();\n this.#deadline = null;\n this.#remainingDelay = null;\n this.#emitter.removeAllListeners();\n this.#destroyed = true;\n }\n\n // ────────── Speed Control ──────────\n\n /**\n * Sets WPM for subsequently scheduled display periods, preserving the input exactly.\n * The corresponding ms-per-item interval is derived without additional rounding.\n */\n public setSpeed(wpm: number): void {\n this.#assertNotDestroyed();\n validateWpm(wpm);\n this.#speed = { unit: \"wpm\", value: wpm };\n }\n\n /**\n * Sets the interval for subsequently scheduled display periods, preserving the input exactly.\n * The corresponding WPM is derived without additional rounding.\n */\n public setMsPerItem(ms: number): void {\n this.#assertNotDestroyed();\n validateMsPerItem(ms);\n this.#speed = { unit: \"msPerItem\", value: ms };\n }\n\n // ────────── State & Snapshot Getters ──────────\n\n /** Current engine state. */\n public get state(): RSVPState {\n return this.#stateMachine.state;\n }\n\n /** Exact WPM input, or the WPM derived from the last ms-per-item input. */\n public get wpm(): number {\n return this.#speed.unit === \"wpm\" ? this.#speed.value : 60_000 / this.#speed.value;\n }\n\n /** Exact ms-per-item input, or the interval derived from the last WPM input. */\n public get msPerItem(): number {\n return this.#speed.unit === \"msPerItem\" ? this.#speed.value : 60_000 / this.#speed.value;\n }\n\n /** Current token index (0-based). */\n public get currentIndex(): number {\n return this.#currentIndex;\n }\n\n /** Current token/item being displayed, or null if none. */\n public get currentItem(): RSVPItem<T> | null {\n return this.#tokens[this.#currentIndex] ?? null;\n }\n\n /** Playback progress (0–1). */\n public get progress(): number {\n if (this.#tokens.length === 0) {\n return 0;\n }\n return this.#hasPresentedCurrent ? (this.#currentIndex + 1) / this.#tokens.length : 0;\n }\n\n /** Total number of tokens/items. */\n public get totalItems(): number {\n return this.#tokens.length;\n }\n\n // ────────── Event System ──────────\n\n /**\n * Subscribes to an engine event. Returns an unsubscribe function.\n */\n public on<K extends RSVPEventType>(\n event: K,\n callback: EventCallback<RSVPEventMap<T>[K]>,\n ): UnsubscribeFn {\n this.#assertNotDestroyed();\n return this.#emitter.on(event, callback);\n }\n\n // ────────── Snapshot ──────────\n\n /**\n * Returns a frozen, readonly snapshot of the full engine state.\n */\n public snapshot(): RSVPSnapshot<T> {\n this.#assertNotDestroyed();\n return Object.freeze({\n state: this.state,\n currentIndex: this.#currentIndex,\n currentItem: this.currentItem,\n progress: this.progress,\n totalItems: this.totalItems,\n wpm: this.wpm,\n msPerItem: this.msPerItem,\n });\n }\n}\n"]}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var v=1,S=6e3,T=10,b=6e4,E=300;var u=class extends Error{from;action;constructor(e,t){super(`Invalid transition: cannot perform '${t}' from '${e}' state.`),this.name="InvalidTransitionError",this.from=e,this.action=t;}},p=class extends Error{constructor(){super("Cannot interact with a destroyed engine instance."),this.name="EngineDestroyedError";}},o=class extends Error{constructor(e){super(e),this.name="InvalidInputError";}},m=class extends Error{index;totalItems;constructor(e,t){super(`Index ${e} is out of bounds (0..${t-1}).`),this.name="IndexOutOfBoundsError",this.index=e,this.totalItems=t;}};var c=class{#e={stateChange:new Set,itemChange:new Set,complete:new Set,error:new Set};on(e,t){let r=this.#e[e];return r.add(t),()=>{r.delete(t);}}emit(e,t){let r=this.#e[e],n=Array.from(r);for(let s of n)try{s(t);}catch(l){if(e!=="error"){let a=l instanceof Error?l:new Error(String(l));this.emit("error",{error:a});}}}removeAllListeners(){for(let e of Object.values(this.#e))e.clear();}};var h=class{now(){let e=globalThis;return typeof e.performance?.now=="function"?e.performance.now():Date.now()}setTimeout(e,t){let r=globalThis;if(typeof r.setTimeout!="function")throw new TypeError("The runtime does not provide setTimeout.");return r.setTimeout(e,t)}clearTimeout(e){let t=globalThis;if(typeof t.clearTimeout!="function")throw new TypeError("The runtime does not provide clearTimeout.");t.clearTimeout(e);}};var d=class{#e;#t=null;#n=null;constructor(e=new h){this.#e=e;}schedule(e,t){if(!Number.isFinite(t)||t<=0)throw new RangeError("delayMs must be a positive finite number.");this.#t!==null&&this.cancel();let r=this.#e.now(),n=t;if(this.#n===null)this.#n=r+t;else {let s=r-this.#n;s>=t?this.#n=r+t:(this.#n+=t,n=Math.max(t/2,t-s));}this.#t=this.#e.setTimeout(()=>{this.#t=null,e();},n);}cancel(){this.#u(),this.#n=null;}#u(){this.#t!==null&&(this.#e.clearTimeout(this.#t),this.#t=null);}};var f=class{#e="IDLE";#t={IDLE:{load:"IDLE",play:"PLAYING",error:"ERROR"},PLAYING:{pause:"PAUSED",stop:"STOPPED",complete:"COMPLETED",error:"ERROR"},PAUSED:{load:"IDLE",play:"PLAYING",stop:"STOPPED",seek:"PAUSED",error:"ERROR"},STOPPED:{load:"IDLE",play:"PLAYING",seek:"PAUSED",error:"ERROR"},COMPLETED:{load:"IDLE",play:"PLAYING",seek:"PAUSED",error:"ERROR"},ERROR:{reset:"IDLE"}};get state(){return this.#e}transition(e){let r=this.#t[this.#e][e];if(!r)throw new u(this.#e,e);return this.#e=r,this.#e}};var y=class{calculate(e){if(!e)return 0;let t=w(e),r=t.filter(({segment:s})=>!x(s));if(r.length===0)return t[0].index;let n=r.length<=3?0:Math.floor(r.length*.25);return r[n].index}};function w(i){let e=globalThis.Intl?.Segmenter;if(typeof e=="function")return Array.from(new e(void 0,{granularity:"grapheme"}).segment(i));let t=[],r=0;for(let n of i){let s=t.at(-1);s&&(/^[\p{M}\uFE0F]$/u.test(n)||n==="\u200D"||s.segment.endsWith("\u200D"))?t[t.length-1]={index:s.index,segment:s.segment+n}:t.push({index:r,segment:n}),r+=n.length;}return t}function x(i){return /^[\p{P}\p{Z}]+$/u.test(i)}var g=class{#e;#t;#n;#u;#c;constructor(e={}){this.#e=e.sentenceDelay??2,this.#t=e.clauseDelay??1.5,this.#n=e.dashDelay??1.3,this.#u=e.nestedTokenize??false,this.#c=e.ovpStrategy??new y,this.#l();}tokenize(e){return e==null?[]:Array.isArray(e)?this.#i(e):typeof e=="string"?this.#r(e):[{value:e,ovpIndex:0,delayMultiplier:this.#s(e)}]}#i(e){return this.#u?e.flatMap(t=>this.tokenize(t)):e.map(t=>({value:t,ovpIndex:typeof t=="string"?this.#c.calculate(t):0,delayMultiplier:this.#s(t)}))}#r(e){let t=e.trim();return t?this.#o(t).map(n=>({value:n,ovpIndex:this.#c.calculate(n),delayMultiplier:this.#s(n)})):[]}#s(e){return typeof e=="string"?/[.!?。!?…]+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#e:/[,;:,、;:]+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#t:/\p{Pd}+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#n:1:1}#l(){let e=[["sentenceDelay",this.#e],["clauseDelay",this.#t],["dashDelay",this.#n]];for(let[t,r]of e)if(!Number.isFinite(r)||r<=0)throw new o(`${t} must be a positive finite number.`)}#o(e){let t=globalThis.Intl?.Segmenter;if(typeof t!="function")return e.split(/\s+/);let r=new t(void 0,{granularity:"word"}),n=[],s="",l=true;for(let a of r.segment(e))/^\s+$/u.test(a.segment)?(s&&n.push(s),s="",l=true):a.isWordLike||!M(a.segment)?(n.push(s+a.segment),s="",l=false):l||s||/^[\p{Ps}\p{Pi}]+$/u.test(a.segment)?s+=a.segment:n[n.length-1]+=a.segment;return s&&n.push(s),n}};function M(i){return /^\p{P}+$/u.test(i)}function I(i){if(!Array.isArray(i))throw new o("tokens must be an array.");for(let[e,t]of i.entries()){if(typeof t!="object"||t===null)throw new o(`Token ${e} must be an object.`);if(!Number.isInteger(t.ovpIndex)||t.ovpIndex<0)throw new o(`Token ${e} has an invalid ovpIndex.`);if(typeof t.value=="string"&&t.ovpIndex>t.value.length)throw new o(`Token ${e} has an out-of-range ovpIndex.`);if(!Number.isFinite(t.delayMultiplier)||t.delayMultiplier<=0)throw new o(`Token ${e} has an invalid delayMultiplier.`)}}function D(i){if(!Number.isFinite(i)||i<1||i>6e3)throw new o(`wpm must be a finite number between ${1} and ${6e3}.`)}function R(i){if(!Number.isFinite(i)||i<10||i>6e4)throw new o(`msPerItem must be a finite number between ${10} and ${6e4}.`)}var P=class{#e;#t;#n;#u;#c;#i=[];#r=0;#s=false;#l=null;#o=null;#h=6e4/300;#d=false;constructor(e={}){this.#e=new f,this.#t=new c,this.#u=e.timeDriver??new h,this.#n=e.scheduler??new d(this.#u),this.#c=e.tokenizer??new g,e.msPerItem!==void 0?this.setMsPerItem(e.msPerItem):e.wpm!==void 0&&this.setSpeed(e.wpm),e.data!==void 0&&this.load(e.data);}load(e){this.#a(),this.#f();try{this.loadTokens(this.#c.tokenize(e));}catch(t){throw t instanceof o||this.#y(this.#m(t)),t}}loadTokens(e){this.#a(),this.#f(),I(e);let t=this.#e.state;this.#e.transition("load"),this.#n.cancel(),this.#S(e),this.#r=0,this.#s=false,this.#l=null,this.#o=null,t!=="IDLE"&&this.#t.emit("stateChange",{previous:t,current:"IDLE"});}#f(){if(this.#e.state==="PLAYING"||this.#e.state==="ERROR")throw new o(`Cannot load data while engine is ${this.#e.state}.`)}#S(e){this.#i=e.map((t,r)=>({value:t.value,index:r,ovpIndex:t.ovpIndex,delayMultiplier:t.delayMultiplier}));}#y(e){let t=this.#e.state;try{this.#e.transition("error");}catch{this.#p(e);return}this.#n.cancel(),this.#l=null,this.#o=null,this.#p(e),this.#t.emit("stateChange",{previous:t,current:"ERROR"});}#p(e){this.#t.emit("error",{error:e});}#m(e){return e instanceof Error?e:new Error(String(e))}#a(){if(this.#d)throw new p}#T=()=>{if(this.#l=null,this.#o=null,this.#e.state==="PLAYING"){if(this.#r>=this.#i.length-1){this.#b();return}this.#r++,this.#g();}};#g(){let e=this.#i[this.#r];this.#s=true,this.#t.emit("itemChange",{item:e,index:this.#r,progress:this.progress,reason:"playback"}),this.#e.state==="PLAYING"&&this.#v(this.#h*e.delayMultiplier);}#v(e){this.#o=null,this.#l=this.#u.now()+e;try{this.#n.schedule(this.#T,e);}catch(t){this.#y(this.#m(t));}}#b(){this.#n.cancel();let e=this.#e.state;this.#e.transition("complete");let t=this.#i[this.#r];this.#t.emit("stateChange",{previous:e,current:"COMPLETED"}),this.#t.emit("complete",{item:t,totalItems:this.#i.length});}play(){this.#a(),this.#P();}#P(){if(this.#i.length===0){this.#p(new o("Cannot play: no tokens loaded."));return}let e=this.#e.state;(e==="STOPPED"||e==="COMPLETED")&&(this.#r=0);try{this.#e.transition("play");}catch(t){this.#p(this.#m(t));return}if(this.#t.emit("stateChange",{previous:e,current:"PLAYING"}),e==="PAUSED"&&this.#s){let t=this.#i[this.#r];this.#v(this.#o??this.#h*t.delayMultiplier);return}this.#s=false,this.#g();}pause(){this.#a();let e=this.#e.state;try{this.#e.transition("pause");}catch(t){this.#p(this.#m(t));return}this.#o=this.#l===null?null:Math.max(0,this.#l-this.#u.now()),this.#n.cancel(),this.#l=null,this.#t.emit("stateChange",{previous:e,current:"PAUSED"});}stop(){this.#a();let e=this.#e.state;try{this.#e.transition("stop");}catch(t){this.#p(this.#m(t));return}this.#n.cancel(),this.#r=0,this.#s=false,this.#l=null,this.#o=null,this.#t.emit("stateChange",{previous:e,current:"STOPPED"});}seek(e){if(this.#a(),e<0||e>=this.#i.length){this.#p(new m(e,this.#i.length));return}let t=this.#e.state;try{this.#e.transition("seek");}catch(r){this.#p(this.#m(r));return}this.#r=e,this.#s=true,this.#o=this.#h*this.#i[e].delayMultiplier,t!=="PAUSED"&&this.#t.emit("stateChange",{previous:t,current:"PAUSED"}),this.#t.emit("itemChange",{item:this.#i[e],index:e,progress:this.progress,reason:"seek"});}next(){if(this.#a(),this.#e.state!=="PAUSED"){this.#t.emit("error",{error:new o("next() is only available in PAUSED state.")});return}this.#r<this.#i.length-1&&(this.#r++,this.#s=true,this.#o=this.#h*this.#i[this.#r].delayMultiplier,this.#t.emit("itemChange",{item:this.#i[this.#r],index:this.#r,progress:this.progress,reason:"next"}));}previous(){if(this.#a(),this.#e.state!=="PAUSED"){this.#t.emit("error",{error:new o("previous() is only available in PAUSED state.")});return}this.#r>0&&(this.#r--,this.#s=true,this.#o=this.#h*this.#i[this.#r].delayMultiplier,this.#t.emit("itemChange",{item:this.#i[this.#r],index:this.#r,progress:this.progress,reason:"previous"}));}reset(){this.#a();let e=this.#e.state;try{this.#e.transition("reset");}catch(t){this.#p(this.#m(t));return}this.#n.cancel(),this.#i=[],this.#r=0,this.#s=false,this.#l=null,this.#o=null,this.#t.emit("stateChange",{previous:e,current:"IDLE"});}destroy(){this.#d||(this.#n.cancel(),this.#l=null,this.#o=null,this.#t.removeAllListeners(),this.#d=true);}setSpeed(e){this.#a(),D(e),this.#h=6e4/e;}setMsPerItem(e){this.#a(),R(e),this.#h=e;}get state(){return this.#e.state}get wpm(){return 6e4/this.#h}get msPerItem(){return this.#h}get currentIndex(){return this.#r}get currentItem(){return this.#i[this.#r]??null}get progress(){return this.#i.length===0?0:this.#s?(this.#r+1)/this.#i.length:0}get totalItems(){return this.#i.length}on(e,t){return this.#a(),this.#t.on(e,t)}snapshot(){return this.#a(),Object.freeze({state:this.state,currentIndex:this.#r,currentItem:this.currentItem,progress:this.progress,totalItems:this.totalItems,wpm:this.wpm,msPerItem:this.#h})}};export{E as DEFAULT_WPM,y as DefaultOVPStrategy,g as DefaultTokenizer,d as DriftCorrectedScheduler,p as EngineDestroyedError,c as EventEmitter,m as IndexOutOfBoundsError,o as InvalidInputError,u as InvalidTransitionError,b as MAX_MS_PER_ITEM,S as MAX_WPM,T as MIN_MS_PER_ITEM,v as MIN_WPM,P as RSVPEngine,f as StateMachine,h as SystemTimeDriver};//# sourceMappingURL=index.js.map
1
+ var g=1,S=6e3,P=10,T=6e4,E=300;var u=class extends Error{from;action;constructor(e,t){super(`Invalid transition: cannot perform '${t}' from '${e}' state.`),this.name="InvalidTransitionError",this.from=e,this.action=t;}},p=class extends Error{constructor(){super("Cannot interact with a destroyed engine instance."),this.name="EngineDestroyedError";}},o=class extends Error{constructor(e){super(e),this.name="InvalidInputError";}},m=class extends Error{index;totalItems;constructor(e,t){super(`Index ${e} is out of bounds (0..${t-1}).`),this.name="IndexOutOfBoundsError",this.index=e,this.totalItems=t;}};var c=class{#e={stateChange:new Set,itemChange:new Set,complete:new Set,error:new Set};on(e,t){let r=this.#e[e];return r.add(t),()=>{r.delete(t);}}emit(e,t){let r=this.#e[e],n=Array.from(r);for(let s of n)try{s(t);}catch(l){if(e!=="error"){let a=l instanceof Error?l:new Error(String(l));this.emit("error",{error:a});}}}removeAllListeners(){for(let e of Object.values(this.#e))e.clear();}};var h=class{now(){let e=globalThis;return typeof e.performance?.now=="function"?e.performance.now():Date.now()}setTimeout(e,t){let r=globalThis;if(typeof r.setTimeout!="function")throw new TypeError("The runtime does not provide setTimeout.");return r.setTimeout(e,t)}clearTimeout(e){let t=globalThis;if(typeof t.clearTimeout!="function")throw new TypeError("The runtime does not provide clearTimeout.");t.clearTimeout(e);}};var d=class{#e;#t=null;#n=null;constructor(e=new h){this.#e=e;}schedule(e,t){if(!Number.isFinite(t)||t<=0)throw new RangeError("delayMs must be a positive finite number.");this.#t!==null&&this.cancel();let r=this.#e.now(),n=t;if(this.#n===null)this.#n=r+t;else {let s=r-this.#n;s>=t?this.#n=r+t:(this.#n+=t,n=Math.max(t/2,t-s));}this.#t=this.#e.setTimeout(()=>{this.#t=null,e();},n);}cancel(){this.#h(),this.#n=null;}#h(){this.#t!==null&&(this.#e.clearTimeout(this.#t),this.#t=null);}};var f=class{#e="IDLE";#t={IDLE:{load:"IDLE",play:"PLAYING",error:"ERROR"},PLAYING:{pause:"PAUSED",stop:"STOPPED",complete:"COMPLETED",error:"ERROR"},PAUSED:{load:"IDLE",play:"PLAYING",stop:"STOPPED",seek:"PAUSED",error:"ERROR"},STOPPED:{load:"IDLE",play:"PLAYING",seek:"PAUSED",error:"ERROR"},COMPLETED:{load:"IDLE",play:"PLAYING",seek:"PAUSED",error:"ERROR"},ERROR:{reset:"IDLE"}};get state(){return this.#e}transition(e){let r=this.#t[this.#e][e];if(!r)throw new u(this.#e,e);return this.#e=r,this.#e}};var y=class{calculate(e){if(!e)return 0;let t=R(e),r=t.filter(({segment:s})=>!x(s));if(r.length===0)return t[0].index;let n=r.length<=3?0:Math.floor(r.length*.25);return r[n].index}};function R(i){let e=globalThis.Intl?.Segmenter;if(typeof e=="function")return Array.from(new e(void 0,{granularity:"grapheme"}).segment(i));let t=[],r=0;for(let n of i){let s=t.at(-1);s&&(/^[\p{M}\uFE0F]$/u.test(n)||n==="\u200D"||s.segment.endsWith("\u200D"))?t[t.length-1]={index:s.index,segment:s.segment+n}:t.push({index:r,segment:n}),r+=n.length;}return t}function x(i){return /^[\p{P}\p{Z}]+$/u.test(i)}var v=class{#e;#t;#n;#h;#c;constructor(e={}){this.#e=e.sentenceDelay??2,this.#t=e.clauseDelay??1.5,this.#n=e.dashDelay??1.3,this.#h=e.nestedTokenize??false,this.#c=e.ovpStrategy??new y,this.#l();}tokenize(e){return e==null?[]:Array.isArray(e)?this.#i(e):typeof e=="string"?this.#r(e):[{value:e,ovpIndex:0,delayMultiplier:this.#s(e)}]}#i(e){return this.#h?e.flatMap(t=>this.tokenize(t)):e.map(t=>({value:t,ovpIndex:typeof t=="string"?this.#c.calculate(t):0,delayMultiplier:this.#s(t)}))}#r(e){let t=e.trim();return t?this.#o(t).map(n=>({value:n,ovpIndex:this.#c.calculate(n),delayMultiplier:this.#s(n)})):[]}#s(e){return typeof e=="string"?/[.!?。!?…]+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#e:/[,;:,、;:]+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#t:/\p{Pd}+[\p{Pe}\p{Pf}"']*$/u.test(e)?this.#n:1:1}#l(){let e=[["sentenceDelay",this.#e],["clauseDelay",this.#t],["dashDelay",this.#n]];for(let[t,r]of e)if(!Number.isFinite(r)||r<=0)throw new o(`${t} must be a positive finite number.`)}#o(e){let t=globalThis.Intl?.Segmenter;if(typeof t!="function")return e.split(/\s+/);let r=new t(void 0,{granularity:"word"}),n=[],s="",l=true;for(let a of r.segment(e))/^\s+$/u.test(a.segment)?(s&&n.push(s),s="",l=true):a.isWordLike||!M(a.segment)?(n.push(s+a.segment),s="",l=false):l||s||/^[\p{Ps}\p{Pi}]+$/u.test(a.segment)?s+=a.segment:n[n.length-1]+=a.segment;return s&&n.push(s),n}};function M(i){return /^\p{P}+$/u.test(i)}function I(i){if(!Array.isArray(i))throw new o("tokens must be an array.");for(let[e,t]of i.entries()){if(typeof t!="object"||t===null)throw new o(`Token ${e} must be an object.`);if(!Number.isInteger(t.ovpIndex)||t.ovpIndex<0)throw new o(`Token ${e} has an invalid ovpIndex.`);if(typeof t.value=="string"&&t.ovpIndex>t.value.length)throw new o(`Token ${e} has an out-of-range ovpIndex.`);if(!Number.isFinite(t.delayMultiplier)||t.delayMultiplier<=0)throw new o(`Token ${e} has an invalid delayMultiplier.`)}}function D(i){if(!Number.isFinite(i)||i<1||i>6e3)throw new o(`wpm must be a finite number between ${1} and ${6e3}.`)}function w(i){if(!Number.isFinite(i)||i<10||i>6e4)throw new o(`msPerItem must be a finite number between ${10} and ${6e4}.`)}var b=class{#e;#t;#n;#h;#c;#i=[];#r=0;#s=false;#l=null;#o=null;#p={unit:"wpm",value:300};#d=false;constructor(e={}){this.#e=new f,this.#t=new c,this.#h=e.timeDriver??new h,this.#n=e.scheduler??new d(this.#h),this.#c=e.tokenizer??new v,e.msPerItem!==void 0?this.setMsPerItem(e.msPerItem):e.wpm!==void 0&&this.setSpeed(e.wpm),e.data!==void 0&&this.load(e.data);}load(e){this.#a(),this.#f();try{this.loadTokens(this.#c.tokenize(e));}catch(t){throw t instanceof o||this.#y(this.#m(t)),t}}loadTokens(e){this.#a(),this.#f(),I(e);let t=this.#e.state;this.#e.transition("load"),this.#n.cancel(),this.#S(e),this.#r=0,this.#s=false,this.#l=null,this.#o=null,t!=="IDLE"&&this.#t.emit("stateChange",{previous:t,current:"IDLE"});}#f(){if(this.#e.state==="PLAYING"||this.#e.state==="ERROR")throw new o(`Cannot load data while engine is ${this.#e.state}.`)}#S(e){this.#i=e.map((t,r)=>({value:t.value,index:r,ovpIndex:t.ovpIndex,delayMultiplier:t.delayMultiplier}));}#y(e){let t=this.#e.state;try{this.#e.transition("error");}catch{this.#u(e);return}this.#n.cancel(),this.#l=null,this.#o=null,this.#u(e),this.#t.emit("stateChange",{previous:t,current:"ERROR"});}#u(e){this.#t.emit("error",{error:e});}#m(e){return e instanceof Error?e:new Error(String(e))}#a(){if(this.#d)throw new p}#P=()=>{if(this.#l=null,this.#o=null,this.#e.state==="PLAYING"){if(this.#r>=this.#i.length-1){this.#T();return}this.#r++,this.#v();}};#v(){let e=this.#i[this.#r];this.#s=true,this.#t.emit("itemChange",{item:e,index:this.#r,progress:this.progress,reason:"playback"}),this.#e.state==="PLAYING"&&this.#g(this.msPerItem*e.delayMultiplier);}#g(e){this.#o=null,this.#l=this.#h.now()+e;try{this.#n.schedule(this.#P,e);}catch(t){this.#y(this.#m(t));}}#T(){this.#n.cancel();let e=this.#e.state;this.#e.transition("complete");let t=this.#i[this.#r];this.#t.emit("stateChange",{previous:e,current:"COMPLETED"}),this.#t.emit("complete",{item:t,totalItems:this.#i.length});}play(){this.#a(),this.#b();}#b(){if(this.#i.length===0){this.#u(new o("Cannot play: no tokens loaded."));return}let e=this.#e.state;(e==="STOPPED"||e==="COMPLETED")&&(this.#r=0);try{this.#e.transition("play");}catch(t){this.#u(this.#m(t));return}if(this.#t.emit("stateChange",{previous:e,current:"PLAYING"}),e==="PAUSED"&&this.#s){let t=this.#i[this.#r];this.#g(this.#o??this.msPerItem*t.delayMultiplier);return}this.#s=false,this.#v();}pause(){this.#a();let e=this.#e.state;try{this.#e.transition("pause");}catch(t){this.#u(this.#m(t));return}this.#o=this.#l===null?null:Math.max(0,this.#l-this.#h.now()),this.#n.cancel(),this.#l=null,this.#t.emit("stateChange",{previous:e,current:"PAUSED"});}stop(){this.#a();let e=this.#e.state;try{this.#e.transition("stop");}catch(t){this.#u(this.#m(t));return}this.#n.cancel(),this.#r=0,this.#s=false,this.#l=null,this.#o=null,this.#t.emit("stateChange",{previous:e,current:"STOPPED"});}seek(e){if(this.#a(),e<0||e>=this.#i.length){this.#u(new m(e,this.#i.length));return}let t=this.#e.state;try{this.#e.transition("seek");}catch(r){this.#u(this.#m(r));return}this.#r=e,this.#s=true,this.#o=this.msPerItem*this.#i[e].delayMultiplier,t!=="PAUSED"&&this.#t.emit("stateChange",{previous:t,current:"PAUSED"}),this.#t.emit("itemChange",{item:this.#i[e],index:e,progress:this.progress,reason:"seek"});}next(){if(this.#a(),this.#e.state!=="PAUSED"){this.#t.emit("error",{error:new o("next() is only available in PAUSED state.")});return}this.#r<this.#i.length-1&&(this.#r++,this.#s=true,this.#o=this.msPerItem*this.#i[this.#r].delayMultiplier,this.#t.emit("itemChange",{item:this.#i[this.#r],index:this.#r,progress:this.progress,reason:"next"}));}previous(){if(this.#a(),this.#e.state!=="PAUSED"){this.#t.emit("error",{error:new o("previous() is only available in PAUSED state.")});return}this.#r>0&&(this.#r--,this.#s=true,this.#o=this.msPerItem*this.#i[this.#r].delayMultiplier,this.#t.emit("itemChange",{item:this.#i[this.#r],index:this.#r,progress:this.progress,reason:"previous"}));}reset(){this.#a();let e=this.#e.state;try{this.#e.transition("reset");}catch(t){this.#u(this.#m(t));return}this.#n.cancel(),this.#i=[],this.#r=0,this.#s=false,this.#l=null,this.#o=null,this.#t.emit("stateChange",{previous:e,current:"IDLE"});}destroy(){this.#d||(this.#n.cancel(),this.#l=null,this.#o=null,this.#t.removeAllListeners(),this.#d=true);}setSpeed(e){this.#a(),D(e),this.#p={unit:"wpm",value:e};}setMsPerItem(e){this.#a(),w(e),this.#p={unit:"msPerItem",value:e};}get state(){return this.#e.state}get wpm(){return this.#p.unit==="wpm"?this.#p.value:6e4/this.#p.value}get msPerItem(){return this.#p.unit==="msPerItem"?this.#p.value:6e4/this.#p.value}get currentIndex(){return this.#r}get currentItem(){return this.#i[this.#r]??null}get progress(){return this.#i.length===0?0:this.#s?(this.#r+1)/this.#i.length:0}get totalItems(){return this.#i.length}on(e,t){return this.#a(),this.#t.on(e,t)}snapshot(){return this.#a(),Object.freeze({state:this.state,currentIndex:this.#r,currentItem:this.currentItem,progress:this.progress,totalItems:this.totalItems,wpm:this.wpm,msPerItem:this.msPerItem})}};export{E as DEFAULT_WPM,y as DefaultOVPStrategy,v as DefaultTokenizer,d as DriftCorrectedScheduler,p as EngineDestroyedError,c as EventEmitter,m as IndexOutOfBoundsError,o as InvalidInputError,u as InvalidTransitionError,T as MAX_MS_PER_ITEM,S as MAX_WPM,P as MIN_MS_PER_ITEM,g as MIN_WPM,b as RSVPEngine,f as StateMachine,h as SystemTimeDriver};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine/config.ts","../src/errors.ts","../src/events/event-emitter.ts","../src/scheduler/system-time-driver.ts","../src/scheduler/drift-corrected-scheduler.ts","../src/state/state-machine.ts","../src/tokenizer/ovp.ts","../src/tokenizer/default-tokenizer.ts","../src/engine/validation.ts","../src/engine/rsvp-engine.ts"],"names":["MIN_WPM","MAX_WPM","MIN_MS_PER_ITEM","MAX_MS_PER_ITEM","DEFAULT_WPM","InvalidTransitionError","from","action","EngineDestroyedError","InvalidInputError","message","IndexOutOfBoundsError","index","totalItems","EventEmitter","#listenerRegistry","event","callback","eventListeners","payload","listenersSnapshot","listener","err","error","SystemTimeDriver","runtimeGlobal","ms","handle","DriftCorrectedScheduler","#timeDriver","#timeoutHandle","#expectedTime","timeDriver","task","delayMs","now","actualDelay","drift","#clearTimer","StateMachine","#state","#transitions","nextState","DefaultOVPStrategy","text","graphemes","segmentGraphemes","content","segment","isPunctuation","target","Segmenter","previous","value","DefaultTokenizer","#sentenceDelay","#clauseDelay","#dashDelay","#nestedTokenize","#ovpStrategy","options","#validateOptions","input","#tokenizeArray","#tokenizeText","#calculateDelay","items","item","trimmed","#segmentWords","word","name","segmenter","tokens","prefix","atBoundary","part","validateTokens","token","validateWpm","wpm","validateMsPerItem","RSVPEngine","#stateMachine","#emitter","#scheduler","#tokenizer","#tokens","#currentIndex","#hasPresentedCurrent","#deadline","#remainingDelay","#msPerItem","#destroyed","data","#assertNotDestroyed","#assertCanLoad","#enterFatalError","#toError","previousState","#setTokens","#reportError","#advance","#completePlayback","#presentCurrent","#scheduleAdvance","delay","#startPlayback"],"mappings":"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","file":"index.js","sourcesContent":["import type { SchedulerStrategy, TimeDriver } from \"../scheduler/types\";\nimport type { TokenizerStrategy } from \"../tokenizer/types\";\n\n/**\n * Configuration options for the RSVP Engine.\n */\nexport interface RSVPEngineOptions<T = string> {\n /** Raw text string, single generic item, or generic item array. */\n readonly data?: T | T[];\n /** Target speed in Words Per Minute. Defaults to 300. */\n readonly wpm?: number;\n /** Direct ms-per-item override. Takes precedence over `wpm`. */\n readonly msPerItem?: number;\n /** Custom tokenizer strategy. Defaults to `DefaultTokenizer`. */\n readonly tokenizer?: TokenizerStrategy<T>;\n /** Custom scheduler strategy. Defaults to `DriftCorrectedScheduler`. */\n readonly scheduler?: SchedulerStrategy;\n /** Clock used to preserve remaining display time across pause/resume. */\n readonly timeDriver?: TimeDriver;\n}\n\n/** Minimum allowed playback speed in Words Per Minute. */\nexport const MIN_WPM = 1;\n/** Maximum allowed playback speed in Words Per Minute. */\nexport const MAX_WPM = 6_000;\n/** Minimum allowed display interval per item in milliseconds. */\nexport const MIN_MS_PER_ITEM = 60_000 / MAX_WPM;\n/** Maximum allowed display interval per item in milliseconds. */\nexport const MAX_MS_PER_ITEM = 60_000 / MIN_WPM;\n/** Default playback speed in Words Per Minute. */\nexport const DEFAULT_WPM = 300;\n","/**\n * Thrown when an invalid state transition is attempted.\n *\n */\nexport class InvalidTransitionError extends Error {\n public readonly from: string;\n public readonly action: string;\n\n constructor(from: string, action: string) {\n super(`Invalid transition: cannot perform '${action}' from '${from}' state.`);\n this.name = \"InvalidTransitionError\";\n this.from = from;\n this.action = action;\n }\n}\n\n/**\n * Thrown when a method is called on a destroyed engine instance.\n */\nexport class EngineDestroyedError extends Error {\n constructor() {\n super(\"Cannot interact with a destroyed engine instance.\");\n this.name = \"EngineDestroyedError\";\n }\n}\n\n/**\n * Thrown when invalid input is provided to the engine (empty text, null data, etc.).\n */\nexport class InvalidInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidInputError\";\n }\n}\n\n/**\n * Thrown when a seek index is out of bounds.\n */\nexport class IndexOutOfBoundsError extends Error {\n public readonly index: number;\n public readonly totalItems: number;\n\n constructor(index: number, totalItems: number) {\n super(`Index ${index} is out of bounds (0..${totalItems - 1}).`);\n this.name = \"IndexOutOfBoundsError\";\n this.index = index;\n this.totalItems = totalItems;\n }\n}\n","import type { EventCallback, RSVPEventMap, RSVPEventType, UnsubscribeFn } from \"./types\";\n\ntype ListenerRegistry<T> = {\n [K in RSVPEventType]: Set<EventCallback<RSVPEventMap<T>[K]>>;\n};\n\nexport class EventEmitter<T = string> {\n readonly #listenerRegistry: ListenerRegistry<T> = {\n stateChange: new Set(),\n itemChange: new Set(),\n complete: new Set(),\n error: new Set(),\n };\n\n public on<K extends RSVPEventType>(\n event: K,\n callback: EventCallback<RSVPEventMap<T>[K]>,\n ): UnsubscribeFn {\n const eventListeners = this.#listenerRegistry[event];\n eventListeners.add(callback);\n\n return () => {\n eventListeners.delete(callback);\n };\n }\n\n public emit<K extends RSVPEventType>(event: K, payload: RSVPEventMap<T>[K]): void {\n const eventListeners = this.#listenerRegistry[event];\n\n // Copy to array to handle safe iteration if unsubscribed during emit\n const listenersSnapshot = Array.from(eventListeners);\n\n for (const listener of listenersSnapshot) {\n try {\n listener(payload);\n } catch (err) {\n if (event !== \"error\") {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit(\"error\", { error });\n }\n }\n }\n }\n\n public removeAllListeners(): void {\n for (const eventListeners of Object.values(this.#listenerRegistry)) {\n eventListeners.clear();\n }\n }\n}\n","import type { TimeDriver } from \"./types\";\nimport type { RuntimeGlobal } from \"../runtimeGlobal\";\n\n/**\n * Default implementation of `TimeDriver` utilizing platform globals\n * (`performance.now` / `Date.now` and `setTimeout` / `clearTimeout`).\n */\nexport class SystemTimeDriver implements TimeDriver {\n public now(): number {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.performance?.now === \"function\") {\n return runtimeGlobal.performance.now();\n }\n return Date.now();\n }\n\n public setTimeout(callback: () => void, ms: number): unknown {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.setTimeout !== \"function\") {\n throw new TypeError(\"The runtime does not provide setTimeout.\");\n }\n return runtimeGlobal.setTimeout(callback, ms);\n }\n\n public clearTimeout(handle: unknown): void {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.clearTimeout !== \"function\") {\n throw new TypeError(\"The runtime does not provide clearTimeout.\");\n }\n runtimeGlobal.clearTimeout(handle);\n }\n}\n","import type { SchedulerStrategy, TimeDriver } from \"./types\";\n\nimport { SystemTimeDriver } from \"./system-time-driver\";\n\n/**\n * High-precision, drift-corrected task scheduler.\n *\n * Implements the {@link SchedulerStrategy} interface by tracking an ideal target timeline\n * (`#expectedTime`) across consecutive task executions. Any latency or jitter introduced\n * by the JavaScript event loop (timer drift) is calculated and subtracted from subsequent\n * intervals, ensuring stable WPM presentation without cumulative time slippage.\n *\n * @example\n * ```ts\n * const scheduler = new DriftCorrectedScheduler();\n *\n * // Schedule a task after 200ms\n * scheduler.schedule(() => {\n * console.log(\"Tick!\");\n * }, 200);\n *\n * // Cancel any pending task\n * scheduler.cancel();\n * ```\n */\nexport class DriftCorrectedScheduler implements SchedulerStrategy {\n readonly #timeDriver: TimeDriver;\n #timeoutHandle: unknown = null;\n #expectedTime: number | null = null;\n\n /**\n * Creates a new instance of `DriftCorrectedScheduler`.\n *\n * @param timeDriver - The underlying time and timer provider. Defaults to {@link SystemTimeDriver}.\n */\n constructor(timeDriver: TimeDriver = new SystemTimeDriver()) {\n this.#timeDriver = timeDriver;\n }\n\n /**\n * Schedules a task to execute after the given delay, compensating for accumulated drift.\n *\n * If a previous task is currently pending, it is automatically cancelled before scheduling\n * the new one. When part of an active session, the actual timeout duration is adjusted\n * by subtracting any detected execution delay (`drift = now - expectedTime`).\n *\n * @param task - The callback function to execute.\n * @param delayMs - The target delay in milliseconds.\n */\n public schedule(task: () => void, delayMs: number): void {\n if (!Number.isFinite(delayMs) || delayMs <= 0) {\n throw new RangeError(\"delayMs must be a positive finite number.\");\n }\n\n if (this.#timeoutHandle !== null) {\n this.cancel();\n }\n\n const now = this.#timeDriver.now();\n let actualDelay = delayMs;\n\n if (this.#expectedTime === null) {\n this.#expectedTime = now + delayMs;\n } else {\n const drift = now - this.#expectedTime;\n if (drift >= delayMs) {\n // Large catch-up bursts make RSVP items unreadable; start a fresh timeline instead.\n this.#expectedTime = now + delayMs;\n } else {\n this.#expectedTime += delayMs;\n actualDelay = Math.max(delayMs / 2, delayMs - drift);\n }\n }\n\n this.#timeoutHandle = this.#timeDriver.setTimeout(() => {\n this.#timeoutHandle = null;\n task();\n }, actualDelay);\n }\n\n /**\n * Cancels any pending scheduled task and resets the session timeline.\n *\n * Calling this method halts the drift compensation timeline so that the next\n * call to {@link schedule} starts a fresh session without previous drift history.\n */\n public cancel(): void {\n this.#clearTimer();\n this.#expectedTime = null;\n }\n\n #clearTimer(): void {\n if (this.#timeoutHandle !== null) {\n this.#timeDriver.clearTimeout(this.#timeoutHandle);\n this.#timeoutHandle = null;\n }\n }\n}\n","import type { RSVPState, StateMachineAction } from \"./types\";\n\nimport { InvalidTransitionError } from \"../errors\";\n\nexport class StateMachine {\n #state: RSVPState = \"IDLE\";\n\n readonly #transitions: Record<RSVPState, Partial<Record<StateMachineAction, RSVPState>>> = {\n IDLE: {\n load: \"IDLE\",\n play: \"PLAYING\",\n error: \"ERROR\",\n },\n PLAYING: {\n pause: \"PAUSED\",\n stop: \"STOPPED\",\n complete: \"COMPLETED\",\n error: \"ERROR\",\n },\n PAUSED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n stop: \"STOPPED\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n STOPPED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n COMPLETED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n ERROR: {\n reset: \"IDLE\",\n },\n };\n\n public get state(): RSVPState {\n return this.#state;\n }\n\n public transition(action: StateMachineAction): RSVPState {\n const allowedTransitions = this.#transitions[this.#state];\n const nextState = allowedTransitions[action];\n\n if (!nextState) {\n throw new InvalidTransitionError(this.#state, action);\n }\n\n this.#state = nextState;\n return this.#state;\n }\n}\n","import type { OVPStrategy } from \"./types\";\n\n/**\n * Default grapheme-aware Optimal Viewing Position strategy.\n */\nexport class DefaultOVPStrategy implements OVPStrategy {\n /**\n * Calculates the Optimal Viewing Position for text.\n * @returns The zero-based UTF-16 offset of the OVP grapheme.\n */\n public calculate(text: string): number {\n if (!text) {\n return 0;\n }\n\n const graphemes = segmentGraphemes(text);\n const content = graphemes.filter(({ segment }) => !isPunctuation(segment));\n\n if (content.length === 0) {\n return graphemes[0].index;\n }\n\n const target = content.length <= 3 ? 0 : Math.floor(content.length * 0.25);\n return content[target].index;\n }\n}\n\ninterface Grapheme {\n readonly index: number;\n readonly segment: string;\n}\n\nfunction segmentGraphemes(text: string): Grapheme[] {\n const Segmenter = globalThis.Intl?.Segmenter;\n if (typeof Segmenter === \"function\") {\n return Array.from(new Segmenter(undefined, { granularity: \"grapheme\" }).segment(text));\n }\n\n const graphemes: Grapheme[] = [];\n let index = 0;\n for (const segment of text) {\n const previous = graphemes.at(-1);\n if (\n previous &&\n (/^[\\p{M}\\uFE0F]$/u.test(segment) ||\n segment === \"\\u200D\" ||\n previous.segment.endsWith(\"\\u200D\"))\n ) {\n graphemes[graphemes.length - 1] = {\n index: previous.index,\n segment: previous.segment + segment,\n };\n } else {\n graphemes.push({ index, segment });\n }\n index += segment.length;\n }\n return graphemes;\n}\n\nfunction isPunctuation(value: string): boolean {\n return /^[\\p{P}\\p{Z}]+$/u.test(value);\n}\n","import type { OVPStrategy, Token, TokenizerStrategy } from \"./types\";\n\nimport { InvalidInputError } from \"../errors\";\n\nimport { DefaultOVPStrategy } from \"./ovp\";\n\n/**\n * Options for configuring `DefaultTokenizer`.\n */\nexport interface DefaultTokenizerOptions {\n /** Multiplier for sentence-ending punctuation (`.`, `!`, `?`). Defaults to `2.0`. */\n readonly sentenceDelay?: number;\n /** Multiplier for clause-ending punctuation (`,`, `;`, `:`). Defaults to `1.5`. */\n readonly clauseDelay?: number;\n /** Multiplier for dashes (`—`, `–`). Defaults to `1.3`. */\n readonly dashDelay?: number;\n /** Whether array elements and nested inputs should be recursively tokenized. Defaults to `false`. */\n readonly nestedTokenize?: boolean;\n /** Strategy used to calculate viewing positions for string tokens. */\n readonly ovpStrategy?: OVPStrategy;\n}\n\n/**\n * Default configurable tokenizer for text, items, and array inputs.\n * Splits text on whitespace and assigns delay multipliers based on punctuation.\n * Wraps custom generic items or arrays into presentable tokens.\n * @typeParam T - The type of items being presented (defaults to `string`).\n */\nexport class DefaultTokenizer<T> implements TokenizerStrategy<T> {\n readonly #sentenceDelay: number;\n readonly #clauseDelay: number;\n readonly #dashDelay: number;\n readonly #nestedTokenize: boolean;\n readonly #ovpStrategy: OVPStrategy;\n\n constructor(options: DefaultTokenizerOptions = {}) {\n this.#sentenceDelay = options.sentenceDelay ?? 2.0;\n this.#clauseDelay = options.clauseDelay ?? 1.5;\n this.#dashDelay = options.dashDelay ?? 1.3;\n this.#nestedTokenize = options.nestedTokenize ?? false;\n this.#ovpStrategy = options.ovpStrategy ?? new DefaultOVPStrategy();\n this.#validateOptions();\n }\n\n public tokenize(input: T | T[]): Token<T>[] {\n if (input === null || input === undefined) {\n return [];\n }\n\n if (Array.isArray(input)) {\n return this.#tokenizeArray(input);\n }\n\n if (typeof input === \"string\") {\n return this.#tokenizeText(input) as Token<T>[];\n }\n\n return [\n {\n value: input,\n ovpIndex: 0,\n delayMultiplier: this.#calculateDelay(input),\n },\n ];\n }\n\n #tokenizeArray(items: T[]): Token<T>[] {\n if (this.#nestedTokenize) {\n return items.flatMap((item) => this.tokenize(item));\n }\n\n return items.map((item) => ({\n value: item,\n ovpIndex: typeof item === \"string\" ? this.#ovpStrategy.calculate(item) : 0,\n delayMultiplier: this.#calculateDelay(item),\n }));\n }\n\n #tokenizeText(text: string): Token<string>[] {\n const trimmed = text.trim();\n if (!trimmed) {\n return [];\n }\n\n const words = this.#segmentWords(trimmed);\n return words.map((word) => ({\n value: word,\n ovpIndex: this.#ovpStrategy.calculate(word),\n delayMultiplier: this.#calculateDelay(word),\n }));\n }\n\n #calculateDelay(item: unknown): number {\n if (typeof item === \"string\") {\n if (/[.!?。!?…]+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#sentenceDelay;\n }\n if (/[,;:,、;:]+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#clauseDelay;\n }\n if (/\\p{Pd}+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#dashDelay;\n }\n return 1.0;\n }\n\n return 1.0;\n }\n\n #validateOptions(): void {\n const options = [\n [\"sentenceDelay\", this.#sentenceDelay],\n [\"clauseDelay\", this.#clauseDelay],\n [\"dashDelay\", this.#dashDelay],\n ] as const;\n for (const [name, value] of options) {\n if (!Number.isFinite(value) || value <= 0) {\n throw new InvalidInputError(`${name} must be a positive finite number.`);\n }\n }\n }\n\n #segmentWords(text: string): string[] {\n const Segmenter = globalThis.Intl?.Segmenter;\n if (typeof Segmenter !== \"function\") {\n return text.split(/\\s+/);\n }\n\n const segmenter = new Segmenter(undefined, { granularity: \"word\" });\n const tokens: string[] = [];\n let prefix = \"\";\n let atBoundary = true;\n for (const part of segmenter.segment(text)) {\n if (/^\\s+$/u.test(part.segment)) {\n if (prefix) {\n tokens.push(prefix);\n }\n prefix = \"\";\n atBoundary = true;\n } else if (part.isWordLike || !isPunctuation(part.segment)) {\n tokens.push(prefix + part.segment);\n prefix = \"\";\n atBoundary = false;\n } else if (atBoundary || prefix || /^[\\p{Ps}\\p{Pi}]+$/u.test(part.segment)) {\n prefix += part.segment;\n } else {\n tokens[tokens.length - 1] += part.segment;\n }\n }\n if (prefix) {\n tokens.push(prefix);\n }\n return tokens;\n }\n}\n\nfunction isPunctuation(value: string): boolean {\n return /^\\p{P}+$/u.test(value);\n}\n","import type { Token } from \"../tokenizer/types\";\n\nimport { InvalidInputError } from \"../errors\";\n\nimport { MAX_MS_PER_ITEM, MAX_WPM, MIN_MS_PER_ITEM, MIN_WPM } from \"./config\";\n\nexport function validateTokens<T>(tokens: Token<T>[]): void {\n if (!Array.isArray(tokens)) {\n throw new InvalidInputError(\"tokens must be an array.\");\n }\n\n for (const [index, token] of tokens.entries()) {\n if (typeof token !== \"object\" || token === null) {\n throw new InvalidInputError(`Token ${index} must be an object.`);\n }\n if (!Number.isInteger(token.ovpIndex) || token.ovpIndex < 0) {\n throw new InvalidInputError(`Token ${index} has an invalid ovpIndex.`);\n }\n if (typeof token.value === \"string\" && token.ovpIndex > token.value.length) {\n throw new InvalidInputError(`Token ${index} has an out-of-range ovpIndex.`);\n }\n if (!Number.isFinite(token.delayMultiplier) || token.delayMultiplier <= 0) {\n throw new InvalidInputError(`Token ${index} has an invalid delayMultiplier.`);\n }\n }\n}\n\nexport function validateWpm(wpm: number): void {\n if (!Number.isFinite(wpm) || wpm < MIN_WPM || wpm > MAX_WPM) {\n throw new InvalidInputError(`wpm must be a finite number between ${MIN_WPM} and ${MAX_WPM}.`);\n }\n}\n\nexport function validateMsPerItem(ms: number): void {\n if (!Number.isFinite(ms) || ms < MIN_MS_PER_ITEM || ms > MAX_MS_PER_ITEM) {\n throw new InvalidInputError(\n `msPerItem must be a finite number between ${MIN_MS_PER_ITEM} and ${MAX_MS_PER_ITEM}.`,\n );\n }\n}\n","import type { RSVPEngineOptions } from \"./config\";\nimport type { RSVPItem, RSVPSnapshot } from \"./types\";\nimport type { EventCallback, RSVPEventMap, RSVPEventType, UnsubscribeFn } from \"../events/types\";\nimport type { SchedulerStrategy, TimeDriver } from \"../scheduler/types\";\nimport type { RSVPState } from \"../state/types\";\nimport type { Token, TokenizerStrategy } from \"../tokenizer/types\";\n\nimport { EngineDestroyedError, IndexOutOfBoundsError, InvalidInputError } from \"../errors\";\nimport { EventEmitter } from \"../events/event-emitter\";\nimport { DriftCorrectedScheduler } from \"../scheduler/drift-corrected-scheduler\";\nimport { SystemTimeDriver } from \"../scheduler/system-time-driver\";\nimport { StateMachine } from \"../state/state-machine\";\nimport { DefaultTokenizer } from \"../tokenizer/default-tokenizer\";\n\nimport { DEFAULT_WPM } from \"./config\";\nimport { validateMsPerItem, validateTokens, validateWpm } from \"./validation\";\n\n/**\n * Headless RSVP Engine — orchestrates state machine, scheduler, tokenizer, and event emitter.\n * @typeParam T - The type of items being presented (defaults to `string`).\n */\nexport class RSVPEngine<T = string> {\n readonly #stateMachine: StateMachine;\n readonly #emitter: EventEmitter<T>;\n readonly #scheduler: SchedulerStrategy;\n readonly #timeDriver: TimeDriver;\n readonly #tokenizer: TokenizerStrategy<T>;\n #tokens: RSVPItem<T>[] = [];\n #currentIndex = 0;\n #hasPresentedCurrent = false;\n #deadline: number | null = null;\n #remainingDelay: number | null = null;\n #msPerItem: number = 60_000 / DEFAULT_WPM;\n #destroyed = false;\n\n constructor(options: RSVPEngineOptions<T> = {}) {\n this.#stateMachine = new StateMachine();\n this.#emitter = new EventEmitter<T>();\n\n this.#timeDriver = options.timeDriver ?? new SystemTimeDriver();\n this.#scheduler = options.scheduler ?? new DriftCorrectedScheduler(this.#timeDriver);\n this.#tokenizer = options.tokenizer ?? new DefaultTokenizer<T>();\n\n // msPerItem takes precedence over wpm\n if (options.msPerItem !== undefined) {\n this.setMsPerItem(options.msPerItem);\n } else if (options.wpm !== undefined) {\n this.setSpeed(options.wpm);\n }\n\n // Constructor failures must remain observable because listeners cannot be attached yet.\n if (options.data !== undefined) {\n this.load(options.data);\n }\n }\n\n // ────────── Tokenization & Loading ──────────\n\n /**\n * Tokenizes and loads raw data into the engine using the configured tokenizer.\n */\n public load(data: T | T[]): void {\n this.#assertNotDestroyed();\n this.#assertCanLoad();\n try {\n this.loadTokens(this.#tokenizer.tokenize(data));\n } catch (error) {\n if (error instanceof InvalidInputError) {\n throw error;\n }\n this.#enterFatalError(this.#toError(error));\n throw error;\n }\n }\n\n /**\n * Directly loads pre-tokenized items into the engine.\n * Useful when tokenization is performed externally (e.g. AI or asynchronous tokenizers).\n */\n public loadTokens(tokens: Token<T>[]): void {\n this.#assertNotDestroyed();\n this.#assertCanLoad();\n validateTokens(tokens);\n const previousState = this.#stateMachine.state;\n this.#stateMachine.transition(\"load\");\n this.#scheduler.cancel();\n this.#setTokens(tokens);\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n if (previousState !== \"IDLE\") {\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"IDLE\" });\n }\n }\n\n #assertCanLoad(): void {\n if (this.#stateMachine.state === \"PLAYING\" || this.#stateMachine.state === \"ERROR\") {\n throw new InvalidInputError(`Cannot load data while engine is ${this.#stateMachine.state}.`);\n }\n }\n\n #setTokens(tokens: Token<T>[]): void {\n this.#tokens = tokens.map((token, index) => ({\n value: token.value,\n index,\n ovpIndex: token.ovpIndex,\n delayMultiplier: token.delayMultiplier,\n }));\n }\n\n // ────────── Error handling ──────────\n\n #enterFatalError(error: Error): void {\n const previousState = this.#stateMachine.state;\n try {\n this.#stateMachine.transition(\"error\");\n } catch {\n this.#reportError(error);\n return;\n }\n this.#scheduler.cancel();\n this.#deadline = null;\n this.#remainingDelay = null;\n this.#reportError(error);\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"ERROR\" });\n }\n\n #reportError(error: Error): void {\n this.#emitter.emit(\"error\", { error });\n }\n\n #toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n\n // ────────── Destroyed guard ──────────\n\n #assertNotDestroyed(): void {\n if (this.#destroyed) {\n throw new EngineDestroyedError();\n }\n }\n\n // ────────── Tick loop ──────────\n\n readonly #advance = (): void => {\n this.#deadline = null;\n this.#remainingDelay = null;\n if (this.#stateMachine.state !== \"PLAYING\") {\n return;\n }\n\n if (this.#currentIndex >= this.#tokens.length - 1) {\n this.#completePlayback();\n return;\n }\n\n this.#currentIndex++;\n this.#presentCurrent();\n };\n\n #presentCurrent(): void {\n const item = this.#tokens[this.#currentIndex];\n this.#hasPresentedCurrent = true;\n\n this.#emitter.emit(\"itemChange\", {\n item,\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"playback\",\n });\n if (this.#stateMachine.state !== \"PLAYING\") {\n return;\n }\n\n this.#scheduleAdvance(this.#msPerItem * item.delayMultiplier);\n }\n\n #scheduleAdvance(delay: number): void {\n this.#remainingDelay = null;\n this.#deadline = this.#timeDriver.now() + delay;\n try {\n this.#scheduler.schedule(this.#advance, delay);\n } catch (error) {\n this.#enterFatalError(this.#toError(error));\n }\n }\n\n #completePlayback(): void {\n this.#scheduler.cancel();\n const previousState = this.#stateMachine.state;\n this.#stateMachine.transition(\"complete\");\n const item = this.#tokens[this.#currentIndex];\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"COMPLETED\" });\n this.#emitter.emit(\"complete\", { item, totalItems: this.#tokens.length });\n }\n\n // ────────── Playback Control ──────────\n\n /**\n * Starts or resumes playback.\n * From STOPPED/COMPLETED: resets index to 0 and starts from beginning.\n * From PAUSED: resumes from current position.\n */\n public play(): void {\n this.#assertNotDestroyed();\n this.#startPlayback();\n }\n\n #startPlayback(): void {\n // Validate we have tokens\n if (this.#tokens.length === 0) {\n this.#reportError(new InvalidInputError(\"Cannot play: no tokens loaded.\"));\n return;\n }\n\n const previousState = this.#stateMachine.state;\n\n // Reset index if coming from STOPPED or COMPLETED\n if (previousState === \"STOPPED\" || previousState === \"COMPLETED\") {\n this.#currentIndex = 0;\n }\n\n try {\n this.#stateMachine.transition(\"play\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PLAYING\",\n });\n\n if (previousState === \"PAUSED\" && this.#hasPresentedCurrent) {\n const item = this.#tokens[this.#currentIndex];\n this.#scheduleAdvance(this.#remainingDelay ?? this.#msPerItem * item.delayMultiplier);\n return;\n }\n\n this.#hasPresentedCurrent = false;\n this.#presentCurrent();\n }\n\n /**\n * Pauses playback, preserving the current index.\n */\n public pause(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"pause\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#remainingDelay =\n this.#deadline === null ? null : Math.max(0, this.#deadline - this.#timeDriver.now());\n this.#scheduler.cancel();\n this.#deadline = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PAUSED\",\n });\n }\n\n /**\n * Stops playback and resets the index to 0.\n */\n public stop(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"stop\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#scheduler.cancel();\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"STOPPED\",\n });\n }\n\n /**\n * Jumps to a specific token index. Transitions to PAUSED state.\n * Only available from PAUSED, STOPPED, or COMPLETED states.\n */\n public seek(index: number): void {\n this.#assertNotDestroyed();\n\n if (index < 0 || index >= this.#tokens.length) {\n this.#reportError(new IndexOutOfBoundsError(index, this.#tokens.length));\n return;\n }\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"seek\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#currentIndex = index;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.#msPerItem * this.#tokens[index].delayMultiplier;\n\n if (previousState !== \"PAUSED\") {\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PAUSED\",\n });\n }\n\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[index],\n index,\n progress: this.progress,\n reason: \"seek\",\n });\n }\n\n /**\n * Advances to the next token. Only available from PAUSED state.\n */\n public next(): void {\n this.#assertNotDestroyed();\n\n if (this.#stateMachine.state !== \"PAUSED\") {\n this.#emitter.emit(\"error\", {\n error: new InvalidInputError(\"next() is only available in PAUSED state.\"),\n });\n return;\n }\n\n if (this.#currentIndex < this.#tokens.length - 1) {\n this.#currentIndex++;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.#msPerItem * this.#tokens[this.#currentIndex].delayMultiplier;\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[this.#currentIndex],\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"next\",\n });\n }\n }\n\n /**\n * Retreats to the previous token. Only available from PAUSED state.\n */\n public previous(): void {\n this.#assertNotDestroyed();\n\n if (this.#stateMachine.state !== \"PAUSED\") {\n this.#emitter.emit(\"error\", {\n error: new InvalidInputError(\"previous() is only available in PAUSED state.\"),\n });\n return;\n }\n\n if (this.#currentIndex > 0) {\n this.#currentIndex--;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.#msPerItem * this.#tokens[this.#currentIndex].delayMultiplier;\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[this.#currentIndex],\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"previous\",\n });\n }\n }\n\n /**\n * Resets engine to IDLE state, clearing all data.\n * Only valid from ERROR state.\n */\n public reset(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"reset\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#scheduler.cancel();\n this.#tokens = [];\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"IDLE\",\n });\n }\n\n /**\n * Clears all timers, removes listeners, and disposes the engine.\n * Required for SPA component unmount cleanup.\n */\n public destroy(): void {\n if (this.#destroyed) {\n return; // idempotent\n }\n this.#scheduler.cancel();\n this.#deadline = null;\n this.#remainingDelay = null;\n this.#emitter.removeAllListeners();\n this.#destroyed = true;\n }\n\n // ────────── Speed Control ──────────\n\n /**\n * Updates playback speed via WPM (live, mid-playback).\n */\n public setSpeed(wpm: number): void {\n this.#assertNotDestroyed();\n validateWpm(wpm);\n this.#msPerItem = 60_000 / wpm;\n }\n\n /**\n * Directly sets ms-per-item interval (live, mid-playback).\n */\n public setMsPerItem(ms: number): void {\n this.#assertNotDestroyed();\n validateMsPerItem(ms);\n this.#msPerItem = ms;\n }\n\n // ────────── State & Snapshot Getters ──────────\n\n /** Current engine state. */\n public get state(): RSVPState {\n return this.#stateMachine.state;\n }\n\n /** Current WPM computed from msPerItem. */\n public get wpm(): number {\n return 60_000 / this.#msPerItem;\n }\n\n /** Current ms-per-item interval. */\n public get msPerItem(): number {\n return this.#msPerItem;\n }\n\n /** Current token index (0-based). */\n public get currentIndex(): number {\n return this.#currentIndex;\n }\n\n /** Current token/item being displayed, or null if none. */\n public get currentItem(): RSVPItem<T> | null {\n return this.#tokens[this.#currentIndex] ?? null;\n }\n\n /** Playback progress (0–1). */\n public get progress(): number {\n if (this.#tokens.length === 0) {\n return 0;\n }\n return this.#hasPresentedCurrent ? (this.#currentIndex + 1) / this.#tokens.length : 0;\n }\n\n /** Total number of tokens/items. */\n public get totalItems(): number {\n return this.#tokens.length;\n }\n\n // ────────── Event System ──────────\n\n /**\n * Subscribes to an engine event. Returns an unsubscribe function.\n */\n public on<K extends RSVPEventType>(\n event: K,\n callback: EventCallback<RSVPEventMap<T>[K]>,\n ): UnsubscribeFn {\n this.#assertNotDestroyed();\n return this.#emitter.on(event, callback);\n }\n\n // ────────── Snapshot ──────────\n\n /**\n * Returns a frozen, readonly snapshot of the full engine state.\n */\n public snapshot(): RSVPSnapshot<T> {\n this.#assertNotDestroyed();\n return Object.freeze({\n state: this.state,\n currentIndex: this.#currentIndex,\n currentItem: this.currentItem,\n progress: this.progress,\n totalItems: this.totalItems,\n wpm: this.wpm,\n msPerItem: this.#msPerItem,\n });\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/engine/config.ts","../src/errors.ts","../src/events/event-emitter.ts","../src/scheduler/system-time-driver.ts","../src/scheduler/drift-corrected-scheduler.ts","../src/state/state-machine.ts","../src/tokenizer/ovp.ts","../src/tokenizer/default-tokenizer.ts","../src/engine/validation.ts","../src/engine/rsvp-engine.ts"],"names":["MIN_WPM","MAX_WPM","MIN_MS_PER_ITEM","MAX_MS_PER_ITEM","DEFAULT_WPM","InvalidTransitionError","from","action","EngineDestroyedError","InvalidInputError","message","IndexOutOfBoundsError","index","totalItems","EventEmitter","#listenerRegistry","event","callback","eventListeners","payload","listenersSnapshot","listener","err","error","SystemTimeDriver","runtimeGlobal","ms","handle","DriftCorrectedScheduler","#timeDriver","#timeoutHandle","#expectedTime","timeDriver","task","delayMs","now","actualDelay","drift","#clearTimer","StateMachine","#state","#transitions","nextState","DefaultOVPStrategy","text","graphemes","segmentGraphemes","content","segment","isPunctuation","target","Segmenter","previous","value","DefaultTokenizer","#sentenceDelay","#clauseDelay","#dashDelay","#nestedTokenize","#ovpStrategy","options","#validateOptions","input","#tokenizeArray","#tokenizeText","#calculateDelay","items","item","trimmed","#segmentWords","word","name","segmenter","tokens","prefix","atBoundary","part","validateTokens","token","validateWpm","wpm","validateMsPerItem","RSVPEngine","#stateMachine","#emitter","#scheduler","#tokenizer","#tokens","#currentIndex","#hasPresentedCurrent","#deadline","#remainingDelay","#speed","#destroyed","data","#assertNotDestroyed","#assertCanLoad","#enterFatalError","#toError","previousState","#setTokens","#reportError","#advance","#completePlayback","#presentCurrent","#scheduleAdvance","delay","#startPlayback"],"mappings":"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","file":"index.js","sourcesContent":["import type { SchedulerStrategy, TimeDriver } from \"../scheduler/types\";\nimport type { TokenizerStrategy } from \"../tokenizer/types\";\n\n/**\n * Configuration options for the RSVP Engine.\n */\nexport interface RSVPEngineOptions<T = string> {\n /** Raw text string, single generic item, or generic item array. */\n readonly data?: T | T[];\n /** Target WPM, preserved exactly unless msPerItem is supplied. Defaults to 300. */\n readonly wpm?: number;\n /** Direct ms-per-item override. Takes precedence over `wpm`. */\n readonly msPerItem?: number;\n /** Custom tokenizer strategy. Defaults to `DefaultTokenizer`. */\n readonly tokenizer?: TokenizerStrategy<T>;\n /** Custom scheduler strategy. Defaults to `DriftCorrectedScheduler`. */\n readonly scheduler?: SchedulerStrategy;\n /** Clock used to preserve remaining display time across pause/resume. */\n readonly timeDriver?: TimeDriver;\n}\n\n/** Minimum allowed playback speed in Words Per Minute. */\nexport const MIN_WPM = 1;\n/** Maximum allowed playback speed in Words Per Minute. */\nexport const MAX_WPM = 6_000;\n/** Minimum allowed display interval per item in milliseconds. */\nexport const MIN_MS_PER_ITEM = 60_000 / MAX_WPM;\n/** Maximum allowed display interval per item in milliseconds. */\nexport const MAX_MS_PER_ITEM = 60_000 / MIN_WPM;\n/** Default playback speed in Words Per Minute. */\nexport const DEFAULT_WPM = 300;\n","/**\n * Thrown when an invalid state transition is attempted.\n *\n */\nexport class InvalidTransitionError extends Error {\n public readonly from: string;\n public readonly action: string;\n\n constructor(from: string, action: string) {\n super(`Invalid transition: cannot perform '${action}' from '${from}' state.`);\n this.name = \"InvalidTransitionError\";\n this.from = from;\n this.action = action;\n }\n}\n\n/**\n * Thrown when a method is called on a destroyed engine instance.\n */\nexport class EngineDestroyedError extends Error {\n constructor() {\n super(\"Cannot interact with a destroyed engine instance.\");\n this.name = \"EngineDestroyedError\";\n }\n}\n\n/**\n * Thrown when invalid input is provided to the engine (empty text, null data, etc.).\n */\nexport class InvalidInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidInputError\";\n }\n}\n\n/**\n * Thrown when a seek index is out of bounds.\n */\nexport class IndexOutOfBoundsError extends Error {\n public readonly index: number;\n public readonly totalItems: number;\n\n constructor(index: number, totalItems: number) {\n super(`Index ${index} is out of bounds (0..${totalItems - 1}).`);\n this.name = \"IndexOutOfBoundsError\";\n this.index = index;\n this.totalItems = totalItems;\n }\n}\n","import type { EventCallback, RSVPEventMap, RSVPEventType, UnsubscribeFn } from \"./types\";\n\ntype ListenerRegistry<T> = {\n [K in RSVPEventType]: Set<EventCallback<RSVPEventMap<T>[K]>>;\n};\n\nexport class EventEmitter<T = string> {\n readonly #listenerRegistry: ListenerRegistry<T> = {\n stateChange: new Set(),\n itemChange: new Set(),\n complete: new Set(),\n error: new Set(),\n };\n\n public on<K extends RSVPEventType>(\n event: K,\n callback: EventCallback<RSVPEventMap<T>[K]>,\n ): UnsubscribeFn {\n const eventListeners = this.#listenerRegistry[event];\n eventListeners.add(callback);\n\n return () => {\n eventListeners.delete(callback);\n };\n }\n\n public emit<K extends RSVPEventType>(event: K, payload: RSVPEventMap<T>[K]): void {\n const eventListeners = this.#listenerRegistry[event];\n\n // Copy to array to handle safe iteration if unsubscribed during emit\n const listenersSnapshot = Array.from(eventListeners);\n\n for (const listener of listenersSnapshot) {\n try {\n listener(payload);\n } catch (err) {\n if (event !== \"error\") {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit(\"error\", { error });\n }\n }\n }\n }\n\n public removeAllListeners(): void {\n for (const eventListeners of Object.values(this.#listenerRegistry)) {\n eventListeners.clear();\n }\n }\n}\n","import type { TimeDriver } from \"./types\";\nimport type { RuntimeGlobal } from \"../runtimeGlobal\";\n\n/**\n * Default implementation of `TimeDriver` utilizing platform globals\n * (`performance.now` / `Date.now` and `setTimeout` / `clearTimeout`).\n */\nexport class SystemTimeDriver implements TimeDriver {\n public now(): number {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.performance?.now === \"function\") {\n return runtimeGlobal.performance.now();\n }\n return Date.now();\n }\n\n public setTimeout(callback: () => void, ms: number): unknown {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.setTimeout !== \"function\") {\n throw new TypeError(\"The runtime does not provide setTimeout.\");\n }\n return runtimeGlobal.setTimeout(callback, ms);\n }\n\n public clearTimeout(handle: unknown): void {\n const runtimeGlobal = globalThis as RuntimeGlobal;\n if (typeof runtimeGlobal.clearTimeout !== \"function\") {\n throw new TypeError(\"The runtime does not provide clearTimeout.\");\n }\n runtimeGlobal.clearTimeout(handle);\n }\n}\n","import type { SchedulerStrategy, TimeDriver } from \"./types\";\n\nimport { SystemTimeDriver } from \"./system-time-driver\";\n\n/**\n * High-precision, drift-corrected task scheduler.\n *\n * Implements the {@link SchedulerStrategy} interface by tracking an ideal target timeline\n * (`#expectedTime`) across consecutive task executions. Any latency or jitter introduced\n * by the JavaScript event loop (timer drift) is calculated and subtracted from subsequent\n * intervals, ensuring stable WPM presentation without cumulative time slippage.\n *\n * @example\n * ```ts\n * const scheduler = new DriftCorrectedScheduler();\n *\n * // Schedule a task after 200ms\n * scheduler.schedule(() => {\n * console.log(\"Tick!\");\n * }, 200);\n *\n * // Cancel any pending task\n * scheduler.cancel();\n * ```\n */\nexport class DriftCorrectedScheduler implements SchedulerStrategy {\n readonly #timeDriver: TimeDriver;\n #timeoutHandle: unknown = null;\n #expectedTime: number | null = null;\n\n /**\n * Creates a new instance of `DriftCorrectedScheduler`.\n *\n * @param timeDriver - The underlying time and timer provider. Defaults to {@link SystemTimeDriver}.\n */\n constructor(timeDriver: TimeDriver = new SystemTimeDriver()) {\n this.#timeDriver = timeDriver;\n }\n\n /**\n * Schedules a task to execute after the given delay, compensating for accumulated drift.\n *\n * If a previous task is currently pending, it is automatically cancelled before scheduling\n * the new one. When part of an active session, the actual timeout duration is adjusted\n * by subtracting any detected execution delay (`drift = now - expectedTime`).\n *\n * @param task - The callback function to execute.\n * @param delayMs - The target delay in milliseconds.\n */\n public schedule(task: () => void, delayMs: number): void {\n if (!Number.isFinite(delayMs) || delayMs <= 0) {\n throw new RangeError(\"delayMs must be a positive finite number.\");\n }\n\n if (this.#timeoutHandle !== null) {\n this.cancel();\n }\n\n const now = this.#timeDriver.now();\n let actualDelay = delayMs;\n\n if (this.#expectedTime === null) {\n this.#expectedTime = now + delayMs;\n } else {\n const drift = now - this.#expectedTime;\n if (drift >= delayMs) {\n // Large catch-up bursts make RSVP items unreadable; start a fresh timeline instead.\n this.#expectedTime = now + delayMs;\n } else {\n this.#expectedTime += delayMs;\n actualDelay = Math.max(delayMs / 2, delayMs - drift);\n }\n }\n\n this.#timeoutHandle = this.#timeDriver.setTimeout(() => {\n this.#timeoutHandle = null;\n task();\n }, actualDelay);\n }\n\n /**\n * Cancels any pending scheduled task and resets the session timeline.\n *\n * Calling this method halts the drift compensation timeline so that the next\n * call to {@link schedule} starts a fresh session without previous drift history.\n */\n public cancel(): void {\n this.#clearTimer();\n this.#expectedTime = null;\n }\n\n #clearTimer(): void {\n if (this.#timeoutHandle !== null) {\n this.#timeDriver.clearTimeout(this.#timeoutHandle);\n this.#timeoutHandle = null;\n }\n }\n}\n","import type { RSVPState, StateMachineAction } from \"./types\";\n\nimport { InvalidTransitionError } from \"../errors\";\n\nexport class StateMachine {\n #state: RSVPState = \"IDLE\";\n\n readonly #transitions: Record<RSVPState, Partial<Record<StateMachineAction, RSVPState>>> = {\n IDLE: {\n load: \"IDLE\",\n play: \"PLAYING\",\n error: \"ERROR\",\n },\n PLAYING: {\n pause: \"PAUSED\",\n stop: \"STOPPED\",\n complete: \"COMPLETED\",\n error: \"ERROR\",\n },\n PAUSED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n stop: \"STOPPED\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n STOPPED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n COMPLETED: {\n load: \"IDLE\",\n play: \"PLAYING\",\n seek: \"PAUSED\",\n error: \"ERROR\",\n },\n ERROR: {\n reset: \"IDLE\",\n },\n };\n\n public get state(): RSVPState {\n return this.#state;\n }\n\n public transition(action: StateMachineAction): RSVPState {\n const allowedTransitions = this.#transitions[this.#state];\n const nextState = allowedTransitions[action];\n\n if (!nextState) {\n throw new InvalidTransitionError(this.#state, action);\n }\n\n this.#state = nextState;\n return this.#state;\n }\n}\n","import type { OVPStrategy } from \"./types\";\n\n/**\n * Default grapheme-aware Optimal Viewing Position strategy.\n */\nexport class DefaultOVPStrategy implements OVPStrategy {\n /**\n * Calculates the Optimal Viewing Position for text.\n * @returns The zero-based UTF-16 offset of the OVP grapheme.\n */\n public calculate(text: string): number {\n if (!text) {\n return 0;\n }\n\n const graphemes = segmentGraphemes(text);\n const content = graphemes.filter(({ segment }) => !isPunctuation(segment));\n\n if (content.length === 0) {\n return graphemes[0].index;\n }\n\n const target = content.length <= 3 ? 0 : Math.floor(content.length * 0.25);\n return content[target].index;\n }\n}\n\ninterface Grapheme {\n readonly index: number;\n readonly segment: string;\n}\n\nfunction segmentGraphemes(text: string): Grapheme[] {\n const Segmenter = globalThis.Intl?.Segmenter;\n if (typeof Segmenter === \"function\") {\n return Array.from(new Segmenter(undefined, { granularity: \"grapheme\" }).segment(text));\n }\n\n const graphemes: Grapheme[] = [];\n let index = 0;\n for (const segment of text) {\n const previous = graphemes.at(-1);\n if (\n previous &&\n (/^[\\p{M}\\uFE0F]$/u.test(segment) ||\n segment === \"\\u200D\" ||\n previous.segment.endsWith(\"\\u200D\"))\n ) {\n graphemes[graphemes.length - 1] = {\n index: previous.index,\n segment: previous.segment + segment,\n };\n } else {\n graphemes.push({ index, segment });\n }\n index += segment.length;\n }\n return graphemes;\n}\n\nfunction isPunctuation(value: string): boolean {\n return /^[\\p{P}\\p{Z}]+$/u.test(value);\n}\n","import type { OVPStrategy, Token, TokenizerStrategy } from \"./types\";\n\nimport { InvalidInputError } from \"../errors\";\n\nimport { DefaultOVPStrategy } from \"./ovp\";\n\n/**\n * Options for configuring `DefaultTokenizer`.\n */\nexport interface DefaultTokenizerOptions {\n /** Multiplier for sentence-ending punctuation (`.`, `!`, `?`). Defaults to `2.0`. */\n readonly sentenceDelay?: number;\n /** Multiplier for clause-ending punctuation (`,`, `;`, `:`). Defaults to `1.5`. */\n readonly clauseDelay?: number;\n /** Multiplier for dashes (`—`, `–`). Defaults to `1.3`. */\n readonly dashDelay?: number;\n /** Whether array elements and nested inputs should be recursively tokenized. Defaults to `false`. */\n readonly nestedTokenize?: boolean;\n /** Strategy used to calculate viewing positions for string tokens. */\n readonly ovpStrategy?: OVPStrategy;\n}\n\n/**\n * Default configurable tokenizer for text, items, and array inputs.\n * Splits text on whitespace and assigns delay multipliers based on punctuation.\n * Wraps custom generic items or arrays into presentable tokens.\n * @typeParam T - The type of items being presented (defaults to `string`).\n */\nexport class DefaultTokenizer<T> implements TokenizerStrategy<T> {\n readonly #sentenceDelay: number;\n readonly #clauseDelay: number;\n readonly #dashDelay: number;\n readonly #nestedTokenize: boolean;\n readonly #ovpStrategy: OVPStrategy;\n\n constructor(options: DefaultTokenizerOptions = {}) {\n this.#sentenceDelay = options.sentenceDelay ?? 2.0;\n this.#clauseDelay = options.clauseDelay ?? 1.5;\n this.#dashDelay = options.dashDelay ?? 1.3;\n this.#nestedTokenize = options.nestedTokenize ?? false;\n this.#ovpStrategy = options.ovpStrategy ?? new DefaultOVPStrategy();\n this.#validateOptions();\n }\n\n public tokenize(input: T | T[]): Token<T>[] {\n if (input === null || input === undefined) {\n return [];\n }\n\n if (Array.isArray(input)) {\n return this.#tokenizeArray(input);\n }\n\n if (typeof input === \"string\") {\n return this.#tokenizeText(input) as Token<T>[];\n }\n\n return [\n {\n value: input,\n ovpIndex: 0,\n delayMultiplier: this.#calculateDelay(input),\n },\n ];\n }\n\n #tokenizeArray(items: T[]): Token<T>[] {\n if (this.#nestedTokenize) {\n return items.flatMap((item) => this.tokenize(item));\n }\n\n return items.map((item) => ({\n value: item,\n ovpIndex: typeof item === \"string\" ? this.#ovpStrategy.calculate(item) : 0,\n delayMultiplier: this.#calculateDelay(item),\n }));\n }\n\n #tokenizeText(text: string): Token<string>[] {\n const trimmed = text.trim();\n if (!trimmed) {\n return [];\n }\n\n const words = this.#segmentWords(trimmed);\n return words.map((word) => ({\n value: word,\n ovpIndex: this.#ovpStrategy.calculate(word),\n delayMultiplier: this.#calculateDelay(word),\n }));\n }\n\n #calculateDelay(item: unknown): number {\n if (typeof item === \"string\") {\n if (/[.!?。!?…]+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#sentenceDelay;\n }\n if (/[,;:,、;:]+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#clauseDelay;\n }\n if (/\\p{Pd}+[\\p{Pe}\\p{Pf}\"']*$/u.test(item)) {\n return this.#dashDelay;\n }\n return 1.0;\n }\n\n return 1.0;\n }\n\n #validateOptions(): void {\n const options = [\n [\"sentenceDelay\", this.#sentenceDelay],\n [\"clauseDelay\", this.#clauseDelay],\n [\"dashDelay\", this.#dashDelay],\n ] as const;\n for (const [name, value] of options) {\n if (!Number.isFinite(value) || value <= 0) {\n throw new InvalidInputError(`${name} must be a positive finite number.`);\n }\n }\n }\n\n #segmentWords(text: string): string[] {\n const Segmenter = globalThis.Intl?.Segmenter;\n if (typeof Segmenter !== \"function\") {\n return text.split(/\\s+/);\n }\n\n const segmenter = new Segmenter(undefined, { granularity: \"word\" });\n const tokens: string[] = [];\n let prefix = \"\";\n let atBoundary = true;\n for (const part of segmenter.segment(text)) {\n if (/^\\s+$/u.test(part.segment)) {\n if (prefix) {\n tokens.push(prefix);\n }\n prefix = \"\";\n atBoundary = true;\n } else if (part.isWordLike || !isPunctuation(part.segment)) {\n tokens.push(prefix + part.segment);\n prefix = \"\";\n atBoundary = false;\n } else if (atBoundary || prefix || /^[\\p{Ps}\\p{Pi}]+$/u.test(part.segment)) {\n prefix += part.segment;\n } else {\n tokens[tokens.length - 1] += part.segment;\n }\n }\n if (prefix) {\n tokens.push(prefix);\n }\n return tokens;\n }\n}\n\nfunction isPunctuation(value: string): boolean {\n return /^\\p{P}+$/u.test(value);\n}\n","import type { Token } from \"../tokenizer/types\";\n\nimport { InvalidInputError } from \"../errors\";\n\nimport { MAX_MS_PER_ITEM, MAX_WPM, MIN_MS_PER_ITEM, MIN_WPM } from \"./config\";\n\nexport function validateTokens<T>(tokens: Token<T>[]): void {\n if (!Array.isArray(tokens)) {\n throw new InvalidInputError(\"tokens must be an array.\");\n }\n\n for (const [index, token] of tokens.entries()) {\n if (typeof token !== \"object\" || token === null) {\n throw new InvalidInputError(`Token ${index} must be an object.`);\n }\n if (!Number.isInteger(token.ovpIndex) || token.ovpIndex < 0) {\n throw new InvalidInputError(`Token ${index} has an invalid ovpIndex.`);\n }\n if (typeof token.value === \"string\" && token.ovpIndex > token.value.length) {\n throw new InvalidInputError(`Token ${index} has an out-of-range ovpIndex.`);\n }\n if (!Number.isFinite(token.delayMultiplier) || token.delayMultiplier <= 0) {\n throw new InvalidInputError(`Token ${index} has an invalid delayMultiplier.`);\n }\n }\n}\n\nexport function validateWpm(wpm: number): void {\n if (!Number.isFinite(wpm) || wpm < MIN_WPM || wpm > MAX_WPM) {\n throw new InvalidInputError(`wpm must be a finite number between ${MIN_WPM} and ${MAX_WPM}.`);\n }\n}\n\nexport function validateMsPerItem(ms: number): void {\n if (!Number.isFinite(ms) || ms < MIN_MS_PER_ITEM || ms > MAX_MS_PER_ITEM) {\n throw new InvalidInputError(\n `msPerItem must be a finite number between ${MIN_MS_PER_ITEM} and ${MAX_MS_PER_ITEM}.`,\n );\n }\n}\n","import type { RSVPEngineOptions } from \"./config\";\nimport type { RSVPItem, RSVPSnapshot } from \"./types\";\nimport type { EventCallback, RSVPEventMap, RSVPEventType, UnsubscribeFn } from \"../events/types\";\nimport type { SchedulerStrategy, TimeDriver } from \"../scheduler/types\";\nimport type { RSVPState } from \"../state/types\";\nimport type { Token, TokenizerStrategy } from \"../tokenizer/types\";\n\nimport { EngineDestroyedError, IndexOutOfBoundsError, InvalidInputError } from \"../errors\";\nimport { EventEmitter } from \"../events/event-emitter\";\nimport { DriftCorrectedScheduler } from \"../scheduler/drift-corrected-scheduler\";\nimport { SystemTimeDriver } from \"../scheduler/system-time-driver\";\nimport { StateMachine } from \"../state/state-machine\";\nimport { DefaultTokenizer } from \"../tokenizer/default-tokenizer\";\n\nimport { DEFAULT_WPM } from \"./config\";\nimport { validateMsPerItem, validateTokens, validateWpm } from \"./validation\";\n\ninterface SpeedSetting {\n readonly unit: \"wpm\" | \"msPerItem\";\n readonly value: number;\n}\n\n/**\n * Headless RSVP Engine — orchestrates state machine, scheduler, tokenizer, and event emitter.\n * @typeParam T - The type of items being presented (defaults to `string`).\n */\nexport class RSVPEngine<T = string> {\n readonly #stateMachine: StateMachine;\n readonly #emitter: EventEmitter<T>;\n readonly #scheduler: SchedulerStrategy;\n readonly #timeDriver: TimeDriver;\n readonly #tokenizer: TokenizerStrategy<T>;\n #tokens: RSVPItem<T>[] = [];\n #currentIndex = 0;\n #hasPresentedCurrent = false;\n #deadline: number | null = null;\n #remainingDelay: number | null = null;\n #speed: SpeedSetting = { unit: \"wpm\", value: DEFAULT_WPM };\n #destroyed = false;\n\n constructor(options: RSVPEngineOptions<T> = {}) {\n this.#stateMachine = new StateMachine();\n this.#emitter = new EventEmitter<T>();\n\n this.#timeDriver = options.timeDriver ?? new SystemTimeDriver();\n this.#scheduler = options.scheduler ?? new DriftCorrectedScheduler(this.#timeDriver);\n this.#tokenizer = options.tokenizer ?? new DefaultTokenizer<T>();\n\n // msPerItem takes precedence over wpm\n if (options.msPerItem !== undefined) {\n this.setMsPerItem(options.msPerItem);\n } else if (options.wpm !== undefined) {\n this.setSpeed(options.wpm);\n }\n\n // Constructor failures must remain observable because listeners cannot be attached yet.\n if (options.data !== undefined) {\n this.load(options.data);\n }\n }\n\n // ────────── Tokenization & Loading ──────────\n\n /**\n * Tokenizes and loads raw data into the engine using the configured tokenizer.\n */\n public load(data: T | T[]): void {\n this.#assertNotDestroyed();\n this.#assertCanLoad();\n try {\n this.loadTokens(this.#tokenizer.tokenize(data));\n } catch (error) {\n if (error instanceof InvalidInputError) {\n throw error;\n }\n this.#enterFatalError(this.#toError(error));\n throw error;\n }\n }\n\n /**\n * Directly loads pre-tokenized items into the engine.\n * Useful when tokenization is performed externally (e.g. AI or asynchronous tokenizers).\n */\n public loadTokens(tokens: Token<T>[]): void {\n this.#assertNotDestroyed();\n this.#assertCanLoad();\n validateTokens(tokens);\n const previousState = this.#stateMachine.state;\n this.#stateMachine.transition(\"load\");\n this.#scheduler.cancel();\n this.#setTokens(tokens);\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n if (previousState !== \"IDLE\") {\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"IDLE\" });\n }\n }\n\n #assertCanLoad(): void {\n if (this.#stateMachine.state === \"PLAYING\" || this.#stateMachine.state === \"ERROR\") {\n throw new InvalidInputError(`Cannot load data while engine is ${this.#stateMachine.state}.`);\n }\n }\n\n #setTokens(tokens: Token<T>[]): void {\n this.#tokens = tokens.map((token, index) => ({\n value: token.value,\n index,\n ovpIndex: token.ovpIndex,\n delayMultiplier: token.delayMultiplier,\n }));\n }\n\n // ────────── Error handling ──────────\n\n #enterFatalError(error: Error): void {\n const previousState = this.#stateMachine.state;\n try {\n this.#stateMachine.transition(\"error\");\n } catch {\n this.#reportError(error);\n return;\n }\n this.#scheduler.cancel();\n this.#deadline = null;\n this.#remainingDelay = null;\n this.#reportError(error);\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"ERROR\" });\n }\n\n #reportError(error: Error): void {\n this.#emitter.emit(\"error\", { error });\n }\n\n #toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n\n // ────────── Destroyed guard ──────────\n\n #assertNotDestroyed(): void {\n if (this.#destroyed) {\n throw new EngineDestroyedError();\n }\n }\n\n // ────────── Tick loop ──────────\n\n readonly #advance = (): void => {\n this.#deadline = null;\n this.#remainingDelay = null;\n if (this.#stateMachine.state !== \"PLAYING\") {\n return;\n }\n\n if (this.#currentIndex >= this.#tokens.length - 1) {\n this.#completePlayback();\n return;\n }\n\n this.#currentIndex++;\n this.#presentCurrent();\n };\n\n #presentCurrent(): void {\n const item = this.#tokens[this.#currentIndex];\n this.#hasPresentedCurrent = true;\n\n this.#emitter.emit(\"itemChange\", {\n item,\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"playback\",\n });\n if (this.#stateMachine.state !== \"PLAYING\") {\n return;\n }\n\n this.#scheduleAdvance(this.msPerItem * item.delayMultiplier);\n }\n\n #scheduleAdvance(delay: number): void {\n this.#remainingDelay = null;\n this.#deadline = this.#timeDriver.now() + delay;\n try {\n this.#scheduler.schedule(this.#advance, delay);\n } catch (error) {\n this.#enterFatalError(this.#toError(error));\n }\n }\n\n #completePlayback(): void {\n this.#scheduler.cancel();\n const previousState = this.#stateMachine.state;\n this.#stateMachine.transition(\"complete\");\n const item = this.#tokens[this.#currentIndex];\n this.#emitter.emit(\"stateChange\", { previous: previousState, current: \"COMPLETED\" });\n this.#emitter.emit(\"complete\", { item, totalItems: this.#tokens.length });\n }\n\n // ────────── Playback Control ──────────\n\n /**\n * Starts or resumes playback.\n * From STOPPED/COMPLETED: resets index to 0 and starts from beginning.\n * From PAUSED: resumes from current position.\n */\n public play(): void {\n this.#assertNotDestroyed();\n this.#startPlayback();\n }\n\n #startPlayback(): void {\n // Validate we have tokens\n if (this.#tokens.length === 0) {\n this.#reportError(new InvalidInputError(\"Cannot play: no tokens loaded.\"));\n return;\n }\n\n const previousState = this.#stateMachine.state;\n\n // Reset index if coming from STOPPED or COMPLETED\n if (previousState === \"STOPPED\" || previousState === \"COMPLETED\") {\n this.#currentIndex = 0;\n }\n\n try {\n this.#stateMachine.transition(\"play\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PLAYING\",\n });\n\n if (previousState === \"PAUSED\" && this.#hasPresentedCurrent) {\n const item = this.#tokens[this.#currentIndex];\n this.#scheduleAdvance(this.#remainingDelay ?? this.msPerItem * item.delayMultiplier);\n return;\n }\n\n this.#hasPresentedCurrent = false;\n this.#presentCurrent();\n }\n\n /**\n * Pauses playback, preserving the current index.\n */\n public pause(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"pause\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#remainingDelay =\n this.#deadline === null ? null : Math.max(0, this.#deadline - this.#timeDriver.now());\n this.#scheduler.cancel();\n this.#deadline = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PAUSED\",\n });\n }\n\n /**\n * Stops playback and resets the index to 0.\n */\n public stop(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"stop\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#scheduler.cancel();\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"STOPPED\",\n });\n }\n\n /**\n * Jumps to a specific token index. Transitions to PAUSED state.\n * Only available from PAUSED, STOPPED, or COMPLETED states.\n */\n public seek(index: number): void {\n this.#assertNotDestroyed();\n\n if (index < 0 || index >= this.#tokens.length) {\n this.#reportError(new IndexOutOfBoundsError(index, this.#tokens.length));\n return;\n }\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"seek\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#currentIndex = index;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.msPerItem * this.#tokens[index].delayMultiplier;\n\n if (previousState !== \"PAUSED\") {\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"PAUSED\",\n });\n }\n\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[index],\n index,\n progress: this.progress,\n reason: \"seek\",\n });\n }\n\n /**\n * Advances to the next token. Only available from PAUSED state.\n */\n public next(): void {\n this.#assertNotDestroyed();\n\n if (this.#stateMachine.state !== \"PAUSED\") {\n this.#emitter.emit(\"error\", {\n error: new InvalidInputError(\"next() is only available in PAUSED state.\"),\n });\n return;\n }\n\n if (this.#currentIndex < this.#tokens.length - 1) {\n this.#currentIndex++;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.msPerItem * this.#tokens[this.#currentIndex].delayMultiplier;\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[this.#currentIndex],\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"next\",\n });\n }\n }\n\n /**\n * Retreats to the previous token. Only available from PAUSED state.\n */\n public previous(): void {\n this.#assertNotDestroyed();\n\n if (this.#stateMachine.state !== \"PAUSED\") {\n this.#emitter.emit(\"error\", {\n error: new InvalidInputError(\"previous() is only available in PAUSED state.\"),\n });\n return;\n }\n\n if (this.#currentIndex > 0) {\n this.#currentIndex--;\n this.#hasPresentedCurrent = true;\n this.#remainingDelay = this.msPerItem * this.#tokens[this.#currentIndex].delayMultiplier;\n this.#emitter.emit(\"itemChange\", {\n item: this.#tokens[this.#currentIndex],\n index: this.#currentIndex,\n progress: this.progress,\n reason: \"previous\",\n });\n }\n }\n\n /**\n * Resets engine to IDLE state, clearing all data.\n * Only valid from ERROR state.\n */\n public reset(): void {\n this.#assertNotDestroyed();\n\n const previousState = this.#stateMachine.state;\n\n try {\n this.#stateMachine.transition(\"reset\");\n } catch (err) {\n this.#reportError(this.#toError(err));\n return;\n }\n\n this.#scheduler.cancel();\n this.#tokens = [];\n this.#currentIndex = 0;\n this.#hasPresentedCurrent = false;\n this.#deadline = null;\n this.#remainingDelay = null;\n\n this.#emitter.emit(\"stateChange\", {\n previous: previousState,\n current: \"IDLE\",\n });\n }\n\n /**\n * Clears all timers, removes listeners, and disposes the engine.\n * Required for SPA component unmount cleanup.\n */\n public destroy(): void {\n if (this.#destroyed) {\n return; // idempotent\n }\n this.#scheduler.cancel();\n this.#deadline = null;\n this.#remainingDelay = null;\n this.#emitter.removeAllListeners();\n this.#destroyed = true;\n }\n\n // ────────── Speed Control ──────────\n\n /**\n * Sets WPM for subsequently scheduled display periods, preserving the input exactly.\n * The corresponding ms-per-item interval is derived without additional rounding.\n */\n public setSpeed(wpm: number): void {\n this.#assertNotDestroyed();\n validateWpm(wpm);\n this.#speed = { unit: \"wpm\", value: wpm };\n }\n\n /**\n * Sets the interval for subsequently scheduled display periods, preserving the input exactly.\n * The corresponding WPM is derived without additional rounding.\n */\n public setMsPerItem(ms: number): void {\n this.#assertNotDestroyed();\n validateMsPerItem(ms);\n this.#speed = { unit: \"msPerItem\", value: ms };\n }\n\n // ────────── State & Snapshot Getters ──────────\n\n /** Current engine state. */\n public get state(): RSVPState {\n return this.#stateMachine.state;\n }\n\n /** Exact WPM input, or the WPM derived from the last ms-per-item input. */\n public get wpm(): number {\n return this.#speed.unit === \"wpm\" ? this.#speed.value : 60_000 / this.#speed.value;\n }\n\n /** Exact ms-per-item input, or the interval derived from the last WPM input. */\n public get msPerItem(): number {\n return this.#speed.unit === \"msPerItem\" ? this.#speed.value : 60_000 / this.#speed.value;\n }\n\n /** Current token index (0-based). */\n public get currentIndex(): number {\n return this.#currentIndex;\n }\n\n /** Current token/item being displayed, or null if none. */\n public get currentItem(): RSVPItem<T> | null {\n return this.#tokens[this.#currentIndex] ?? null;\n }\n\n /** Playback progress (0–1). */\n public get progress(): number {\n if (this.#tokens.length === 0) {\n return 0;\n }\n return this.#hasPresentedCurrent ? (this.#currentIndex + 1) / this.#tokens.length : 0;\n }\n\n /** Total number of tokens/items. */\n public get totalItems(): number {\n return this.#tokens.length;\n }\n\n // ────────── Event System ──────────\n\n /**\n * Subscribes to an engine event. Returns an unsubscribe function.\n */\n public on<K extends RSVPEventType>(\n event: K,\n callback: EventCallback<RSVPEventMap<T>[K]>,\n ): UnsubscribeFn {\n this.#assertNotDestroyed();\n return this.#emitter.on(event, callback);\n }\n\n // ────────── Snapshot ──────────\n\n /**\n * Returns a frozen, readonly snapshot of the full engine state.\n */\n public snapshot(): RSVPSnapshot<T> {\n this.#assertNotDestroyed();\n return Object.freeze({\n state: this.state,\n currentIndex: this.#currentIndex,\n currentItem: this.currentItem,\n progress: this.progress,\n totalItems: this.totalItems,\n wpm: this.wpm,\n msPerItem: this.msPerItem,\n });\n }\n}\n"]}
package/package.json CHANGED
@@ -1,24 +1,26 @@
1
1
  {
2
2
  "name": "@rsvp-engine/core",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Headless, zero-dependency RSVP engine with drift-corrected scheduling and pluggable tokenization",
5
- "license": "MIT",
5
+ "keywords": [
6
+ "rsvp",
7
+ "speed-reading",
8
+ "rapid-serial-visual-presentation",
9
+ "orp",
10
+ "spritz",
11
+ "reader-engine",
12
+ "text-pacing",
13
+ "headless",
14
+ "typescript"
15
+ ],
6
16
  "repository": {
7
17
  "type": "git",
8
18
  "url": "git+https://github.com/altunmustafa/rsvp-engine.git",
9
19
  "directory": "packages/core"
10
20
  },
11
- "engines": {
12
- "node": ">=18"
13
- },
14
- "publishConfig": {
15
- "access": "public",
16
- "provenance": true
17
- },
21
+ "license": "MIT",
22
+ "sideEffects": false,
18
23
  "type": "module",
19
- "main": "./dist/index.cjs",
20
- "module": "./dist/index.js",
21
- "types": "./dist/index.d.ts",
22
24
  "exports": {
23
25
  ".": {
24
26
  "import": {
@@ -31,6 +33,9 @@
31
33
  }
32
34
  }
33
35
  },
36
+ "main": "./dist/index.cjs",
37
+ "module": "./dist/index.js",
38
+ "types": "./dist/index.d.ts",
34
39
  "files": [
35
40
  "dist",
36
41
  "scripts",
@@ -41,37 +46,35 @@
41
46
  ],
42
47
  "dependencies": {},
43
48
  "devDependencies": {
44
- "typescript": "^6.0.3",
45
- "tsup": "^8.5.1",
46
- "vitest": "^4.1.10",
49
+ "@types/node": "^26.2.0",
50
+ "@vitest/coverage-v8": "^4.1.10",
47
51
  "eslint": "^10.8.1",
48
52
  "prettier": "^3.9.6",
53
+ "sort-package-json": "^4.0.0",
54
+ "tsup": "^8.5.1",
55
+ "typescript": "^6.0.3",
49
56
  "typescript-eslint": "^8.67.0",
50
- "@types/node": "^26.2.0",
51
- "@vitest/coverage-v8": "^4.1.10"
57
+ "vitest": "^4.1.10"
58
+ },
59
+ "engines": {
60
+ "node": ">=18"
61
+ },
62
+ "publishConfig": {
63
+ "access": "public",
64
+ "provenance": true
52
65
  },
53
- "sideEffects": false,
54
- "keywords": [
55
- "rsvp",
56
- "speed-reading",
57
- "rapid-serial-visual-presentation",
58
- "orp",
59
- "spritz",
60
- "reader-engine",
61
- "text-pacing",
62
- "headless",
63
- "typescript"
64
- ],
65
66
  "scripts": {
66
67
  "build": "tsup && tsc --emitDeclarationOnly --declaration --outDir dist && cp dist/index.d.ts dist/index.d.cts",
67
- "test": "vitest run",
68
- "test:coverage": "vitest run --coverage",
69
- "lint": "eslint src/",
70
- "format": "prettier . --write --ignore-path ../../.gitignore",
71
- "format:check": "prettier . --check --ignore-path ../../.gitignore",
72
- "typecheck": "tsc --noEmit",
73
68
  "check:architecture": "node scripts/check-architecture.js",
69
+ "check:package": "node scripts/check-package.js",
74
70
  "check:size": "node scripts/check-bundle-size.js",
75
- "check:package": "node scripts/check-package.js"
71
+ "format": "prettier . --write --ignore-path ../../.gitignore",
72
+ "format:check": "prettier . --check --ignore-path ../../.gitignore",
73
+ "format:package-json": "sort-package-json package.json",
74
+ "format:package-json:check": "sort-package-json package.json --check",
75
+ "lint": "eslint src/",
76
+ "test": "vitest run",
77
+ "test:coverage": "vitest run --coverage",
78
+ "typecheck": "tsc --noEmit"
76
79
  }
77
80
  }