@youversion/platform-core 1.20.1 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ import { T as TransformedBibleHtml } from './browser-DzQ1yOHv.cjs';
2
+
3
+ /**
4
+ * Transforms Bible HTML for server environments using linkedom.
5
+ *
6
+ * Import from `@youversion/platform-core/server` to avoid bundling linkedom
7
+ * in client-side builds.
8
+ *
9
+ * linkedom requires HTML to be wrapped in body tags for `doc.body.innerHTML`
10
+ * to work correctly, so this function handles that wrapping automatically.
11
+ *
12
+ * @param html - The raw Bible HTML from the YouVersion API
13
+ * @returns The transformed HTML
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { transformBibleHtml } from '@youversion/platform-core/server';
18
+ *
19
+ * const result = transformBibleHtml(rawHtml);
20
+ * console.log(result.html); // Clean HTML with self-contained footnote anchors
21
+ * ```
22
+ */
23
+ declare function transformBibleHtml(html: string): TransformedBibleHtml;
24
+
25
+ export { TransformedBibleHtml, transformBibleHtml };
@@ -0,0 +1,25 @@
1
+ import { T as TransformedBibleHtml } from './browser-DzQ1yOHv.js';
2
+
3
+ /**
4
+ * Transforms Bible HTML for server environments using linkedom.
5
+ *
6
+ * Import from `@youversion/platform-core/server` to avoid bundling linkedom
7
+ * in client-side builds.
8
+ *
9
+ * linkedom requires HTML to be wrapped in body tags for `doc.body.innerHTML`
10
+ * to work correctly, so this function handles that wrapping automatically.
11
+ *
12
+ * @param html - The raw Bible HTML from the YouVersion API
13
+ * @returns The transformed HTML
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { transformBibleHtml } from '@youversion/platform-core/server';
18
+ *
19
+ * const result = transformBibleHtml(rawHtml);
20
+ * console.log(result.html); // Clean HTML with self-contained footnote anchors
21
+ * ```
22
+ */
23
+ declare function transformBibleHtml(html: string): TransformedBibleHtml;
24
+
25
+ export { TransformedBibleHtml, transformBibleHtml };
package/dist/server.js ADDED
@@ -0,0 +1,18 @@
1
+ import {
2
+ transformBibleHtml
3
+ } from "./chunk-2Z2S2WY3.js";
4
+
5
+ // src/bible-html-transformer-server.ts
6
+ import { DOMParser } from "linkedom";
7
+ function transformBibleHtml2(html) {
8
+ return transformBibleHtml(html, {
9
+ parseHtml: (h) => new DOMParser().parseFromString(
10
+ `<html><body>${h}</body></html>`,
11
+ "text/html"
12
+ ),
13
+ serializeHtml: (doc) => doc.body.innerHTML
14
+ });
15
+ }
16
+ export {
17
+ transformBibleHtml2 as transformBibleHtml
18
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youversion/platform-core",
3
- "version": "1.20.1",
3
+ "version": "1.21.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -19,6 +19,16 @@
19
19
  "types": "./dist/index.d.ts",
20
20
  "import": "./dist/index.js",
21
21
  "require": "./dist/index.cjs"
22
+ },
23
+ "./browser": {
24
+ "types": "./dist/browser.d.ts",
25
+ "import": "./dist/browser.js",
26
+ "require": "./dist/browser.cjs"
27
+ },
28
+ "./server": {
29
+ "types": "./dist/server.d.ts",
30
+ "import": "./dist/server.js",
31
+ "require": "./dist/server.cjs"
22
32
  }
23
33
  },
24
34
  "devDependencies": {
@@ -33,12 +43,20 @@
33
43
  "@internal/eslint-config": "0.0.0",
34
44
  "@internal/tsconfig": "0.0.0"
35
45
  },
46
+ "peerDependencies": {
47
+ "linkedom": "^0.18.12"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "linkedom": {
51
+ "optional": true
52
+ }
53
+ },
36
54
  "dependencies": {
37
55
  "zod": "4.1.12"
38
56
  },
39
57
  "scripts": {
40
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
41
- "build": "tsup src/index.ts --format cjs,esm --dts",
58
+ "dev": "tsup src/index.ts src/browser.ts src/server.ts --format cjs,esm --dts --watch",
59
+ "build": "tsup src/index.ts src/browser.ts src/server.ts --format cjs,esm --dts",
42
60
  "lint": "eslint . --max-warnings 0",
43
61
  "typecheck": "tsc --noEmit",
44
62
  "test": "dotenv -e .env.local -- vitest run",
@@ -0,0 +1,37 @@
1
+ import { DOMParser } from 'linkedom';
2
+
3
+ import {
4
+ transformBibleHtml as transformBibleHtmlWithAdapters,
5
+ type TransformedBibleHtml,
6
+ } from './bible-html-transformer';
7
+
8
+ /**
9
+ * Transforms Bible HTML for server environments using linkedom.
10
+ *
11
+ * Import from `@youversion/platform-core/server` to avoid bundling linkedom
12
+ * in client-side builds.
13
+ *
14
+ * linkedom requires HTML to be wrapped in body tags for `doc.body.innerHTML`
15
+ * to work correctly, so this function handles that wrapping automatically.
16
+ *
17
+ * @param html - The raw Bible HTML from the YouVersion API
18
+ * @returns The transformed HTML
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * import { transformBibleHtml } from '@youversion/platform-core/server';
23
+ *
24
+ * const result = transformBibleHtml(rawHtml);
25
+ * console.log(result.html); // Clean HTML with self-contained footnote anchors
26
+ * ```
27
+ */
28
+ export function transformBibleHtml(html: string): TransformedBibleHtml {
29
+ return transformBibleHtmlWithAdapters(html, {
30
+ parseHtml: (h: string) =>
31
+ new DOMParser().parseFromString(
32
+ `<html><body>${h}</body></html>`,
33
+ 'text/html',
34
+ ) as unknown as Document,
35
+ serializeHtml: (doc: Document) => doc.body.innerHTML,
36
+ });
37
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @vitest-environment node
3
+ */
4
+ import { describe, it, expect } from 'vitest';
5
+ import { transformBibleHtml } from './bible-html-transformer-server';
6
+
7
+ describe('transformBibleHtml', () => {
8
+ it('should transform HTML using linkedom', () => {
9
+ const html = `
10
+ <div>
11
+ <div class="p">
12
+ <span class="yv-v" v="1"></span><span class="yv-vlbl">1</span>Verse text<span class="yv-n f"><span class="ft">Note</span></span>.
13
+ </div>
14
+ </div>
15
+ `;
16
+
17
+ const result = transformBibleHtml(html);
18
+
19
+ expect(result.html).toBeDefined();
20
+ expect(result.html).toContain('data-verse-footnote="1"');
21
+ });
22
+
23
+ it('should handle empty HTML', () => {
24
+ const result = transformBibleHtml('');
25
+
26
+ expect(result.html).toBeDefined();
27
+ });
28
+
29
+ it('should embed footnote content in data-verse-footnote-content', () => {
30
+ const html = `
31
+ <div>
32
+ <div class="p">
33
+ <span class="yv-v" v="1"></span><span class="yv-vlbl">1</span>Text<span class="yv-n f"><span class="ft">First note</span></span>.
34
+ <span class="yv-v" v="2"></span><span class="yv-vlbl">2</span>More text<span class="yv-n f"><span class="ft">Second note</span></span>.
35
+ </div>
36
+ </div>
37
+ `;
38
+
39
+ const result = transformBibleHtml(html);
40
+
41
+ expect(result.html).toContain('data-verse-footnote="1"');
42
+ expect(result.html).toContain('data-verse-footnote="2"');
43
+ expect(result.html).toContain('First note');
44
+ expect(result.html).toContain('Second note');
45
+ });
46
+
47
+ it('should wrap verse content in .yv-v[v] elements', () => {
48
+ const html = `
49
+ <div>
50
+ <div class="p">
51
+ <span class="yv-v" v="1"></span><span class="yv-vlbl">1</span>Verse one text.
52
+ </div>
53
+ <div class="p">
54
+ <span class="yv-v" v="2"></span><span class="yv-vlbl">2</span>Verse two text.
55
+ </div>
56
+ </div>
57
+ `;
58
+
59
+ const result = transformBibleHtml(html);
60
+
61
+ // linkedom may serialize attributes in different order than browsers
62
+ expect(result.html).toContain('class="yv-v"');
63
+ expect(result.html).toContain('v="1"');
64
+ expect(result.html).toContain('v="2"');
65
+ expect(result.html).toContain('Verse one text.');
66
+ expect(result.html).toContain('Verse two text.');
67
+ });
68
+
69
+ it('should add non-breaking space after verse labels', () => {
70
+ const html = `
71
+ <div>
72
+ <div class="p">
73
+ <span class="yv-v" v="1"></span><span class="yv-vlbl">1</span>Verse text.
74
+ </div>
75
+ </div>
76
+ `;
77
+
78
+ const result = transformBibleHtml(html);
79
+
80
+ // linkedom encodes non-breaking space as &#160; instead of the raw character
81
+ expect(result.html).toMatch(/1(\u00A0|&#160;)/);
82
+ });
83
+
84
+ it('should handle intro chapter footnotes', () => {
85
+ const html = `
86
+ <div>
87
+ <div class="ip">Some intro text<span class="yv-n f"><span class="ft">First note</span></span> and more text<span class="yv-n f"><span class="ft">Second note</span></span>.</div>
88
+ </div>
89
+ `;
90
+
91
+ const result = transformBibleHtml(html);
92
+
93
+ expect(result.html).toContain('data-verse-footnote="intro-0"');
94
+ expect(result.html).toContain('data-verse-footnote="intro-1"');
95
+ expect(result.html).toContain('First note');
96
+ expect(result.html).toContain('Second note');
97
+ });
98
+
99
+ it('should include data-verse-footnote-content attribute', () => {
100
+ const html = `
101
+ <div>
102
+ <div class="p">
103
+ <span class="yv-v" v="1"></span><span class="yv-vlbl">1</span>Verse text<span class="yv-n f"><span class="ft">See Rashi</span></span>.
104
+ </div>
105
+ </div>
106
+ `;
107
+
108
+ const result = transformBibleHtml(html);
109
+
110
+ expect(result.html).toContain('data-verse-footnote-content=');
111
+ expect(result.html).toContain('See Rashi');
112
+ });
113
+
114
+ it('should return html property only', () => {
115
+ const html = '<div>Test</div>';
116
+ const result = transformBibleHtml(html);
117
+
118
+ expect(result).toHaveProperty('html');
119
+ expect(result).not.toHaveProperty('notes');
120
+ expect(typeof result.html).toBe('string');
121
+ });
122
+
123
+ it('should remove script tags entirely', () => {
124
+ const html = '<p>Safe text</p><script>alert("XSS")</script>';
125
+ const result = transformBibleHtml(html);
126
+
127
+ expect(result.html).not.toContain('script');
128
+ expect(result.html).not.toContain('alert');
129
+ expect(result.html).toContain('Safe text');
130
+ });
131
+
132
+ it('should strip event handler attributes', () => {
133
+ const html = '<p onclick="alert(\'XSS\')">Click me</p>';
134
+ const result = transformBibleHtml(html);
135
+
136
+ expect(result.html).not.toContain('onclick');
137
+ expect(result.html).toContain('Click me');
138
+ });
139
+
140
+ it('should preserve safe Bible HTML through linkedom', () => {
141
+ const html = `
142
+ <div class="p">
143
+ <span class="wj">Jesus said</span>
144
+ </div>
145
+ `;
146
+ const result = transformBibleHtml(html);
147
+
148
+ expect(result.html).toContain('class="wj"');
149
+ expect(result.html).toContain('Jesus said');
150
+ });
151
+ });