Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fresh-papayas-display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@youversion/platform-core': minor
---

Add a declarative passage display API that returns transformed Bible HTML,
current attribution, required stylesheet descriptors, and container attributes
without rendering or mutating the DOM.
19 changes: 19 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ A verse or contiguous verse range in one chapter of one Bible version,
identified by a USFM string (`JHN.3.16`, `JHN.3.16-18`). A chapter USFM
(`JHN.3`) is a passage *scope* used for querying, not a highlightable unit.

## Passage ID

The canonical string identifier for a passage, expressed in USFM format, such
as `JHN.3.16` or `JHN.3.16-18`. _Avoid_: USFM, reference string.

## Passage display model

A declarative representation of a passage and everything a web application
needs to display it with YouVersion's rendering contract. _Avoid_: rendered
passage, passage component, display bundle.

## Passage display model attribution

The current, non-empty legal text returned by the passage display model. The
short copyright text is preferred; promotional content is its fallback. This
fail-closed contract applies to `getPassageDisplay`; existing React UI
components retain their own attribution behavior.
_Avoid_: copyright HTML.

## Bible version

A translation/edition of the Bible, identified by a numeric id. The SDK
Expand Down
1 change: 1 addition & 0 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The `<major>` path segment is defined in **one place**: the `packages/ui/CDN_CSS

- **Do not bump it for routine releases.** The file at `/platform/<major>/bible.css` is overwritten in place with each UI package release.
- **Bump it only when the CSS changes in a breaking way** (selectors/variables/class names that existing consumers depend on are removed or behave differently). Bumping starts publishing to a new `/platform/<major+1>/bible.css` URL and leaves the old file untouched for existing consumers.
- **Keep core synchronized.** `pnpm check:cdn-css-major` verifies that the URL returned by `getBibleStylesheets` uses the same major. Root lint and CI run this guard.

### Feature flag: `feature.platform.sdkCssCdn`

Expand Down
2 changes: 1 addition & 1 deletion docs/adding-a-core-endpoint.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export runtime-agnostic while `linkedom` stays out of browser bundles:
|---|---|
| `@youversion/platform-core` | Runtime-agnostic; requires DOM adapters |
| `@youversion/platform-core/browser` | Convenience wrapper using native `DOMParser` |
| `@youversion/platform-core/server` | Convenience wrapper using `linkedom` |
| `@youversion/platform-core/server` | Convenience wrapper using `jsdom` |

If a new client needs DOM access, follow the same pattern rather than importing
a DOM library into the main entry point.
8 changes: 8 additions & 0 deletions docs/adr/0008-return-declarative-passage-display-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Return declarative passage display models from core

`@youversion/platform-core` will provide a high-level passage display operation
that returns transformed HTML, current attribution, stylesheet descriptors, and
container attributes as data. Core will fetch but will not render, inject
resources, mutate the DOM, or cache attribution, preserving its
framework-agnostic boundary while making the correct rendering path difficult
to misuse.
102 changes: 102 additions & 0 deletions docs/passage-display-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Passage display API

## Purpose

`getPassageDisplay` gives non-React web applications one supported operation
for retrieving transformed Bible HTML, current display attribution, and the
resources required to apply YouVersion's Bible presentation. The result is
declarative and works with browser frameworks, server-rendered templates, and
plain JavaScript.

The operation complements the granular `getPassage` and `getVersion` methods;
it does not replace them.

## Public API

```ts
type GetPassageDisplayOptions = Readonly<{
versionId: number;
passageId: string;
includeHeadings?: boolean;
includeNotes?: boolean;
}>;

type PassageStylesheet = Readonly<{
kind: "bible" | "font";
rel: "stylesheet";
href: string;
}>;

type BiblePassageDisplay = Readonly<{
version: BibleVersion;
html: string;
attribution: {
text: string;
source: "copyright" | "promotionalContent";
};
stylesheets: readonly PassageStylesheet[];
containerAttributes: {
"data-yv-sdk": "";
"data-slot": "yv-bible-renderer";
};
}>;

const display = await bibleClient.getPassageDisplay({
versionId: 3034,
passageId: "JHN.3.16",
includeHeadings: true,
includeNotes: true,
});
```

The module also exports `getPassageDisplay(client, options)` for the
tree-shakable functional API, `getBibleStylesheets(config)` for applications
that install global resources once, and stable constants for the Bible CSS URL,
Untitled Serif font ID, and container attributes.

## Behavior

- The operation always requests HTML and always transforms it. Callers that
need text or raw API HTML use `getPassage`.
- Passage content and Bible version metadata are fetched concurrently when the
active version filter can decide from the numeric id alone.
- A language filter requires version metadata. In that case, the version is
validated before Scripture is fetched, and that same response supplies the
display model. No duplicate metadata request is made.
- Passage display model attribution is freshly requested for every operation
and is never cached by this API. This contract is scoped to
`getPassageDisplay`; it does not redefine existing React UI component
behavior.
- Non-empty `copyright` is preferred. Non-empty `promotional_content` is the
fallback. If neither exists, `MissingPassageAttributionError` rejects the
operation so a caller cannot receive a display-ready passage without legal
text.
- The font stylesheet URL uses font ID `1`, respects the configured API host,
and URL-encodes the app key. Untitled Serif is the intended first-choice font;
Source Serif 4 remains the CSS fallback.
- The operation does not create elements, inject stylesheets, mutate global
state, or cache data.

## Environment behavior

Browser transformation uses the platform `DOMParser`. Server transformation
uses the existing dynamic `jsdom` path and therefore requires the documented
optional peer dependency. Zero-configuration server dependency design is a
separate concern and does not expand this API.

## Failure behavior

HTTP, timeout, input-validation, version-filter, and transformation failures
flow through their existing paths. Missing attribution throws
`MissingPassageAttributionError`, which exposes:

```ts
readonly code = "missing_passage_attribution";
readonly versionId: number;
```

## Non-goals

The first version does not expose text format, raw HTML, transformation opt-out,
theme selection, CSS overrides, DOM targets, resource injection, cache policy,
or transformer dependency injection.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"test": "turbo test",
"test:coverage": "pnpm --filter @youversion/platform-core test:coverage && pnpm --filter @youversion/platform-react-hooks test:coverage && pnpm --filter @youversion/platform-react-ui test:coverage",
"test:watch": "turbo test:watch",
"lint": "turbo build --filter=@youversion/platform-react-hooks && oxlint",
"lint": "turbo build --filter=@youversion/platform-react-hooks && pnpm check:cdn-css-major && oxlint",
"typecheck": "turbo typecheck",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"changeset": "changeset",
Expand All @@ -36,6 +36,7 @@
"size:why": "node scripts/bundle-visualize.mjs",
"size:visualize": "node scripts/bundle-visualize.mjs",
"check:tree-shaking": "node scripts/check-tree-shaking.mjs",
"check:cdn-css-major": "node scripts/check-cdn-css-major.mjs",
"analyze": "node scripts/analyze.mjs",
"analyze:select": "node scripts/analyze-select.mjs",
"generate:i18n": "pnpm --filter @youversion/platform-react-ui generate:i18n",
Expand Down
5 changes: 5 additions & 0 deletions packages/core/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ bible-chapter.ts # getVersion/getChapter + shared id/book/chapter pa
bible-reads.ts # Book/chapter/verse/VOTD reads (tree-shakable module)
bible-versions.ts # Version listing (tree-shakable module)
bible-passage.ts # Passage fetch (tree-shakable module)
bible-display-resources.ts # Dependency-free Bible CSS/font resource descriptors
bible-passage-display.ts # Declarative styled-passage model and resources
languages.ts # LanguagesClient - language data (facade over languages-* modules)
languages-language.ts # Single-language fetch (tree-shakable module)
languages-list.ts # Language listing (tree-shakable module)
Expand Down Expand Up @@ -51,6 +53,9 @@ index.ts # Main entry point (runtime-agnostic)
- `setStorageItem()`, `removeStorageItem()`, `clearStorage()`: Throw-safe mutations for a resolved store (`setStorageItem` returns whether the write landed)
- `transformBibleHtml`: Runtime-agnostic Bible HTML transformer (requires DOM adapters)
- `TransformBibleHtmlOptions`: Options for DOM parsing and serialization
- `getPassageDisplay`: Fetch transformed HTML, current attribution, and declarative rendering resources
- `getBibleStylesheets`: Build the ordered Bible CSS and Fonts API stylesheet descriptors
- `MissingPassageAttributionError`: Fail-closed error when a version has no display attribution

### Browser CSS (`@youversion/platform-core/browser/styles/*`)
- `index.css`: All-in-one import (fonts + theme + bible-reader)
Expand Down
23 changes: 23 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ const passage = await bibleClient.getPassage(versions.data[0].id, 'JHN.3.16');
console.log(passage.content);
```

### Display Bible HTML

Use `getPassageDisplay` when you need transformed HTML together with current
attribution and the resources required to apply YouVersion's Bible styles:

```ts
const display = await bibleClient.getPassageDisplay({
versionId: 3034,
passageId: 'JHN.3.16',
});

for (const stylesheet of display.stylesheets) {
console.log(stylesheet.href);
}

console.log(display.html);
console.log(display.attribution.text);
```

The result is declarative. The SDK does not insert the stylesheets or HTML into
your page. On a server, HTML transformation requires the optional `jsdom` peer
dependency.

## Documentation and API Reference
* [developers.youversion.com/sdks/typescript](https://developers.youversion.com/sdks/typescript)

Expand Down
4 changes: 4 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
"!dist/bible-versions.cjs",
"!dist/bible-passage.js",
"!dist/bible-passage.cjs",
"!dist/bible-display-resources.js",
"!dist/bible-display-resources.cjs",
"!dist/bible-passage-display.js",
"!dist/bible-passage-display.cjs",
"!dist/client.js",
"!dist/client.cjs",
"!dist/languages.js",
Expand Down
Loading
Loading