diff --git a/c-sharp-tests/Projects/VersificationServiceTests.cs b/c-sharp-tests/Projects/ParatextProjectDataProviderVersificationTests.cs
similarity index 62%
rename from c-sharp-tests/Projects/VersificationServiceTests.cs
rename to c-sharp-tests/Projects/ParatextProjectDataProviderVersificationTests.cs
index ba6ba161e8b..2494ee53a23 100644
--- a/c-sharp-tests/Projects/VersificationServiceTests.cs
+++ b/c-sharp-tests/Projects/ParatextProjectDataProviderVersificationTests.cs
@@ -5,42 +5,42 @@
namespace TestParanextDataProvider.Projects
{
///
- /// Unit tests for .
+ /// Unit tests for the versification methods
+ /// — i.e. the methods registered under the platformScripture.Versification
+ /// projectInterface:
///
- ///
- /// The service exposes three RPC functions that delegate to libpalaso's
- /// ScrVers via ScrText.Settings.Versification:
- ///
///
- /// - LookupFinalVerseNumber — passthrough.
- /// - LookupFinalChapter — passthrough.
- /// - LookupFinalVerseNumbersInBook — passthrough plus
- /// bookkeeping: returns int[lastChapter + 1] with index 0 unused
- /// and indices 1..lastChapter populated. The off-by-one boundary
- /// is the most worth testing.
+ /// - LookupFinalVerseNumber(bookNum, chapterNum) — passthrough to
+ /// ScrText.Settings.Versification.GetLastVerse.
+ /// - LookupFinalChapter(bookNum) — passthrough to
+ /// ScrText.Settings.Versification.GetLastChapter.
+ /// - LookupFinalVerseNumbersInBook(bookNum) — passthrough plus bookkeeping:
+ /// returns int[lastChapter + 1] with index 0 a filler 0 and indices
+ /// 1..lastChapter populated for ergonomic result[chapterNum] access.
+ /// The off-by-one boundary is the most worth testing.
///
///
///
- /// Setup follows the existing pattern: a
- /// is created and registered via
- /// ParatextProjects.FakeAddProject, then resolved through the
- /// production LocalParatextProjects.GetParatextProject static
- /// lookup that uses internally. Each
- /// test compares the service result against the underlying versification
- /// lookups directly so the assertions remain valid regardless of whether
- /// the project's default versification is English, Original, etc.
+ /// Setup follows the same pattern used by :
+ /// a is created and registered via
+ /// ParatextProjects.FakeAddProject, then we construct a
+ /// that subclasses the real PDP and gives
+ /// us direct in-process access to the registered methods. Each test compares the PDP
+ /// result against the underlying versification lookups directly so the assertions stay
+ /// valid regardless of which versification scheme the dummy project happens to default to.
///
///
[ExcludeFromCodeCoverage]
[TestFixture]
- internal class VersificationServiceTests : PapiTestBase
+ internal class ParatextProjectDataProviderVersificationTests : PapiTestBase
{
+ private const string PdpName = "versificationTestProject";
private const int GenesisBookNum = 1;
private const int PhilemonBookNum = 57;
private ScrText _scrText = null!;
private ProjectDetails _projectDetails = null!;
- private VersificationService _service = null!;
+ private DummyParatextProjectDataProvider _provider = null!;
[SetUp]
public override async Task TestSetupAsync()
@@ -51,7 +51,12 @@ public override async Task TestSetupAsync()
_projectDetails = CreateProjectDetails(_scrText);
ParatextProjects.FakeAddProject(_projectDetails, _scrText);
- _service = new VersificationService(Client);
+ _provider = new DummyParatextProjectDataProvider(
+ PdpName,
+ Client,
+ _projectDetails,
+ ParatextProjects
+ );
}
[TearDown]
@@ -68,7 +73,8 @@ public void TearDown()
[Test]
[Description(
"For a multi-chapter book (Genesis, 50 chapters), the returned array length "
- + "must be lastChapter + 1 — index 0 reserved as 'unused'."
+ + "must be lastChapter + 1 — index 0 reserved as a filler so consumers can "
+ + "index by 1-based chapter number directly."
)]
public void LookupFinalVerseNumbersInBook_Genesis_ReturnsArrayOfLengthLastChapterPlusOne()
{
@@ -76,16 +82,13 @@ public void LookupFinalVerseNumbersInBook_Genesis_ReturnsArrayOfLengthLastChapte
GenesisBookNum
);
- var result = _service.LookupFinalVerseNumbersInBook(
- _projectDetails.Metadata.Id,
- GenesisBookNum
- );
+ var result = _provider.LookupFinalVerseNumbersInBook(GenesisBookNum);
Assert.That(result, Is.Not.Null);
Assert.That(
result.Length,
Is.EqualTo(expectedLastChapter + 1),
- "array length must be lastChapter + 1 (index 0 reserved as unused)"
+ "array length must be lastChapter + 1 (index 0 reserved as filler)"
);
}
@@ -95,10 +98,7 @@ public void LookupFinalVerseNumbersInBook_Genesis_ReturnsArrayOfLengthLastChapte
)]
public void LookupFinalVerseNumbersInBook_Genesis_IndexZeroIsUnusedAndDefaults()
{
- var result = _service.LookupFinalVerseNumbersInBook(
- _projectDetails.Metadata.Id,
- GenesisBookNum
- );
+ var result = _provider.LookupFinalVerseNumbersInBook(GenesisBookNum);
Assert.That(result[0], Is.EqualTo(0), "index 0 is unused — must be default(int)");
}
@@ -112,10 +112,7 @@ public void LookupFinalVerseNumbersInBook_Genesis_IndexOneMatchesGetLastVerseOfC
{
int expected = _scrText.Settings.Versification.GetLastVerse(GenesisBookNum, 1);
- var result = _service.LookupFinalVerseNumbersInBook(
- _projectDetails.Metadata.Id,
- GenesisBookNum
- );
+ var result = _provider.LookupFinalVerseNumbersInBook(GenesisBookNum);
Assert.That(
result[1],
@@ -137,10 +134,7 @@ public void LookupFinalVerseNumbersInBook_Genesis_LastIndexMatchesGetLastVerseOf
lastChapter
);
- var result = _service.LookupFinalVerseNumbersInBook(
- _projectDetails.Metadata.Id,
- GenesisBookNum
- );
+ var result = _provider.LookupFinalVerseNumbersInBook(GenesisBookNum);
Assert.That(
result[lastChapter],
@@ -160,10 +154,7 @@ public void LookupFinalVerseNumbersInBook_Genesis_AllChaptersMatchVersificationL
var versification = _scrText.Settings.Versification;
int lastChapter = versification.GetLastChapter(GenesisBookNum);
- var result = _service.LookupFinalVerseNumbersInBook(
- _projectDetails.Metadata.Id,
- GenesisBookNum
- );
+ var result = _provider.LookupFinalVerseNumbersInBook(GenesisBookNum);
for (int chapter = 1; chapter <= lastChapter; chapter++)
{
@@ -178,7 +169,7 @@ public void LookupFinalVerseNumbersInBook_Genesis_AllChaptersMatchVersificationL
[Test]
[Description(
"For a single-chapter book (Philemon), the returned array length must be 2 "
- + "(index 0 unused, index 1 = last verse of chapter 1)."
+ + "(index 0 filler, index 1 = last verse of chapter 1)."
)]
public void LookupFinalVerseNumbersInBook_Philemon_ReturnsArrayOfLengthTwo()
{
@@ -193,13 +184,10 @@ public void LookupFinalVerseNumbersInBook_Philemon_ReturnsArrayOfLengthTwo()
1
);
- var result = _service.LookupFinalVerseNumbersInBook(
- _projectDetails.Metadata.Id,
- PhilemonBookNum
- );
+ var result = _provider.LookupFinalVerseNumbersInBook(PhilemonBookNum);
Assert.That(result.Length, Is.EqualTo(2), "single-chapter book -> length 2");
- Assert.That(result[0], Is.EqualTo(0), "index 0 is unused");
+ Assert.That(result[0], Is.EqualTo(0), "index 0 is filler");
Assert.That(
result[1],
Is.EqualTo(expectedLastVerse),
@@ -211,8 +199,8 @@ public void LookupFinalVerseNumbersInBook_Philemon_ReturnsArrayOfLengthTwo()
// LookupFinalVerseNumber & LookupFinalChapter — passthrough wiring.
//
// These methods delegate directly to libpalaso. The tests below are
- // sanity checks confirming the wiring (project lookup -> versification
- // -> result) is intact, not re-tests of libpalaso.
+ // sanity checks confirming the wiring (PDP -> project lookup ->
+ // versification -> result) is intact, not re-tests of libpalaso.
// =====================================================================
[Test]
@@ -224,11 +212,7 @@ public void LookupFinalVerseNumber_Genesis1_MatchesUnderlyingVersification()
{
int expected = _scrText.Settings.Versification.GetLastVerse(GenesisBookNum, 1);
- int actual = _service.LookupFinalVerseNumber(
- _projectDetails.Metadata.Id,
- GenesisBookNum,
- 1
- );
+ int actual = _provider.LookupFinalVerseNumber(GenesisBookNum, 1);
Assert.That(actual, Is.EqualTo(expected));
}
@@ -242,35 +226,9 @@ public void LookupFinalChapter_Genesis_MatchesUnderlyingVersification()
{
int expected = _scrText.Settings.Versification.GetLastChapter(GenesisBookNum);
- int actual = _service.LookupFinalChapter(_projectDetails.Metadata.Id, GenesisBookNum);
+ int actual = _provider.LookupFinalChapter(GenesisBookNum);
Assert.That(actual, Is.EqualTo(expected));
}
-
- // =====================================================================
- // Unknown projectId — error propagation.
- //
- // LocalParatextProjects.GetParatextProject delegates to
- // ScrTextCollection.GetById which throws ProjectNotFoundException for
- // an id with no matching project. The service does not catch it, so it
- // must propagate to the caller. (See ParatextProjectDataProviderFactoryTests
- // for the same pattern at the factory layer.)
- // =====================================================================
-
- [Test]
- [Description(
- "An unknown projectId propagates ProjectNotFoundException from "
- + "LocalParatextProjects.GetParatextProject."
- )]
- public void LookupFinalVerseNumbersInBook_UnknownProjectId_ThrowsProjectNotFoundException()
- {
- // "00" is the canonical 'no such project' id used in the existing
- // ParatextProjectDataProviderFactoryTests fixture.
- const string unknownProjectId = "00";
-
- Assert.Throws(
- () => _service.LookupFinalVerseNumbersInBook(unknownProjectId, GenesisBookNum)
- );
- }
}
}
diff --git a/c-sharp/Program.cs b/c-sharp/Program.cs
index 89b7a6b3638..fa42023642c 100644
--- a/c-sharp/Program.cs
+++ b/c-sharp/Program.cs
@@ -80,7 +80,6 @@ public static async Task Main()
var dblResources = new DblResourcesDataProvider(papi);
var paratextRegistrationService = new ParatextRegistrationService(papi);
var checklistNetworkObject = new ChecklistNetworkObject(papi);
- var versificationService = new VersificationService(papi);
var manageBooksService = new ManageBooksService(
papi,
paratextProjects,
@@ -93,7 +92,6 @@ await Task.WhenAll(
dblResources.RegisterDataProviderAsync(),
paratextRegistrationService.InitializeAsync(),
checklistNetworkObject.InitializeAsync(),
- versificationService.InitializeAsync(),
manageBooksService.RegisterNetworkObjectAsync()
);
diff --git a/c-sharp/Projects/LocalParatextProjects.cs b/c-sharp/Projects/LocalParatextProjects.cs
index 8f9276eddd4..90f6e23bd88 100644
--- a/c-sharp/Projects/LocalParatextProjects.cs
+++ b/c-sharp/Projects/LocalParatextProjects.cs
@@ -44,6 +44,7 @@ internal class LocalParatextProjects
ProjectInterfaces.USX_VERSE,
ProjectInterfaces.PLAIN_TEXT_VERSE,
ProjectInterfaces.MARKER_NAMES,
+ ProjectInterfaces.VERSIFICATION,
];
public LocalParatextProjects()
diff --git a/c-sharp/Projects/ParatextProjectDataProvider.cs b/c-sharp/Projects/ParatextProjectDataProvider.cs
index 88e37140ff1..d2c9d3f29d2 100644
--- a/c-sharp/Projects/ParatextProjectDataProvider.cs
+++ b/c-sharp/Projects/ParatextProjectDataProvider.cs
@@ -110,6 +110,10 @@ LocalParatextProjects paratextProjects
retVal.Add(("getMarkerNames", GetMarkerNames));
+ retVal.Add(("lookupFinalVerseNumber", LookupFinalVerseNumber));
+ retVal.Add(("lookupFinalChapter", LookupFinalChapter));
+ retVal.Add(("lookupFinalVerseNumbersInBook", LookupFinalVerseNumbersInBook));
+
return retVal;
}
@@ -1356,6 +1360,48 @@ public string[] GetMarkerNames(int bookNum)
#endregion
+ #region Versification (platformScripture.Versification)
+
+ ///
+ /// Returns the final verse number in the specified book and chapter using the project's
+ /// versification. Each call reads ScrText.Settings.Versification fresh, so results
+ /// reflect any in-session changes to the project's versification setting.
+ ///
+ public int LookupFinalVerseNumber(int bookNum, int chapterNum)
+ {
+ var scrText = LocalParatextProjects.GetParatextProject(ProjectDetails.Metadata.Id);
+ return scrText.Settings.Versification.GetLastVerse(bookNum, chapterNum);
+ }
+
+ ///
+ /// Returns the final chapter number in the specified book using the project's versification.
+ ///
+ public int LookupFinalChapter(int bookNum)
+ {
+ var scrText = LocalParatextProjects.GetParatextProject(ProjectDetails.Metadata.Id);
+ return scrText.Settings.Versification.GetLastChapter(bookNum);
+ }
+
+ ///
+ /// Returns the final verse number for each chapter in the specified book using the project's
+ /// versification. Index n is the last verse number in chapter n (1-based);
+ /// index 0 is a filler 0 so callers can use result[chapterNum] without
+ /// off-by-one. The returned array has length lastChapter + 1. Useful for pre-fetching
+ /// a whole book in one round trip.
+ ///
+ public int[] LookupFinalVerseNumbersInBook(int bookNum)
+ {
+ var scrText = LocalParatextProjects.GetParatextProject(ProjectDetails.Metadata.Id);
+ var versification = scrText.Settings.Versification;
+ int lastChapter = versification.GetLastChapter(bookNum);
+ int[] result = new int[lastChapter + 1];
+ for (int chapter = 1; chapter <= lastChapter; chapter++)
+ result[chapter] = versification.GetLastVerse(bookNum, chapter);
+ return result;
+ }
+
+ #endregion
+
#region USX
public string GetBookUsx(VerseRef verseRef)
diff --git a/c-sharp/Projects/ProjectInterfaces.cs b/c-sharp/Projects/ProjectInterfaces.cs
index a4151b12f2d..e5638f98d89 100644
--- a/c-sharp/Projects/ProjectInterfaces.cs
+++ b/c-sharp/Projects/ProjectInterfaces.cs
@@ -19,4 +19,5 @@ public static class ProjectInterfaces
public const string PLAIN_TEXT_VERSE = "platformScripture.PlainText_Verse";
public const string LEGACY_COMMENT = "legacyCommentManager.comments";
public const string MARKER_NAMES = "platformScripture.MarkerNames";
+ public const string VERSIFICATION = "platformScripture.Versification";
}
diff --git a/c-sharp/Projects/VersificationService.cs b/c-sharp/Projects/VersificationService.cs
deleted file mode 100644
index f45657e502c..00000000000
--- a/c-sharp/Projects/VersificationService.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using Paranext.DataProvider.NetworkObjects;
-
-namespace Paranext.DataProvider.Projects;
-
-///
-/// Network object exposing each project's versification lookups (last chapter per book,
-/// last verse per chapter). Read-only; delegates to libpalaso's ScrVers via
-/// ScrText.Settings.Versification.
-///
-internal class VersificationService : NetworkObject
-{
- private const string NetworkObjectName = "platformScripture.versificationService";
-
- public VersificationService(PapiClient papiClient)
- : base(papiClient) { }
-
- public async Task InitializeAsync()
- {
- List<(string functionName, Delegate function)> functions =
- [
- ("lookupFinalVerseNumber", LookupFinalVerseNumber),
- ("lookupFinalChapter", LookupFinalChapter),
- ("lookupFinalVerseNumbersInBook", LookupFinalVerseNumbersInBook),
- ];
-
- await RegisterNetworkObjectAsync(
- NetworkObjectName,
- functions,
- new NetworkObjectCreatedDetails
- {
- Id = NetworkObjectName,
- ObjectType = NetworkObjectType.OBJECT,
- FunctionNames = [.. functions.Select(f => f.functionName)],
- }
- );
- }
-
- ///
- /// Returns the final verse number in the specified book and chapter using the project's
- /// versification.
- ///
- public int LookupFinalVerseNumber(string projectId, int bookNum, int chapterNum)
- {
- var scrText = LocalParatextProjects.GetParatextProject(projectId);
- return scrText.Settings.Versification.GetLastVerse(bookNum, chapterNum);
- }
-
- ///
- /// Returns the final chapter number in the specified book using the project's versification.
- ///
- public int LookupFinalChapter(string projectId, int bookNum)
- {
- var scrText = LocalParatextProjects.GetParatextProject(projectId);
- return scrText.Settings.Versification.GetLastChapter(bookNum);
- }
-
- ///
- /// Returns the final verse number for each chapter in the specified book using the project's
- /// versification. Index n is the last verse number in chapter n (1-based);
- /// index 0 is unused. Useful for pre-fetching a whole book in one round trip.
- ///
- public int[] LookupFinalVerseNumbersInBook(string projectId, int bookNum)
- {
- var scrText = LocalParatextProjects.GetParatextProject(projectId);
- var versification = scrText.Settings.Versification;
- int lastChapter = versification.GetLastChapter(bookNum);
- int[] result = new int[lastChapter + 1];
- for (int chapter = 1; chapter <= lastChapter; chapter++)
- result[chapter] = versification.GetLastVerse(bookNum, chapter);
- return result;
- }
-}
diff --git a/extensions/src/platform-scripture-editor/src/platform-scripture-editor.web-view.tsx b/extensions/src/platform-scripture-editor/src/platform-scripture-editor.web-view.tsx
index aa4f34d23b6..4ccaabe05bb 100644
--- a/extensions/src/platform-scripture-editor/src/platform-scripture-editor.web-view.tsx
+++ b/extensions/src/platform-scripture-editor/src/platform-scripture-editor.web-view.tsx
@@ -26,7 +26,6 @@ import {
useProjectSetting,
useRecentScriptureRefs,
} from '@papi/frontend/react';
-import type { IVersificationService } from 'platform-scripture';
import { Canon, SerializedVerseRef } from '@sillsdev/scripture';
import type { CommandHandlers, CommandNames } from 'papi-shared-types';
import {
@@ -349,21 +348,18 @@ globalThis.webViewComponent = function PlatformScriptureEditor({
// verse selection. When the book changes we refetch; for books other than the current one we do
// not offer verse selection (the picker falls back to chapter-level submission).
const currentBookNum = useMemo(() => Canon.bookIdToNumber(scrRef.book), [scrRef.book]);
+ const versificationPdp = useProjectDataProvider('platformScripture.Versification', projectId);
const fetchLastVersesInCurrentBook = useCallback(async (): Promise => {
- if (!projectId || currentBookNum <= 0) return undefined;
+ if (!versificationPdp || currentBookNum <= 0) return undefined;
try {
- const versificationService = await papi.networkObjects.get(
- 'platformScripture.versificationService',
- );
- if (!versificationService) return undefined;
- return await versificationService.lookupFinalVerseNumbersInBook(projectId, currentBookNum);
+ return await versificationPdp.lookupFinalVerseNumbersInBook(currentBookNum);
} catch (err) {
logger.debug(
`Failed to fetch verse counts for book ${currentBookNum}: ${getErrorMessage(err)}`,
);
return undefined;
}
- }, [projectId, currentBookNum]);
+ }, [versificationPdp, currentBookNum]);
const [lastVersesInCurrentBook] = usePromise(fetchLastVersesInCurrentBook, undefined);
const getEndVerse = useCallback(
(bookId: string, chapterNum: number): number => {
diff --git a/extensions/src/platform-scripture/src/checklist.web-view.tsx b/extensions/src/platform-scripture/src/checklist.web-view.tsx
index d2f9e4b7a7d..8ff1102ee64 100644
--- a/extensions/src/platform-scripture/src/checklist.web-view.tsx
+++ b/extensions/src/platform-scripture/src/checklist.web-view.tsx
@@ -1,6 +1,6 @@
import { WebViewProps } from '@papi/core';
import papi, { logger, network } from '@papi/frontend';
-import { useData, useLocalizedStrings } from '@papi/frontend/react';
+import { useData, useLocalizedStrings, useProjectDataProvider } from '@papi/frontend/react';
import {
useEvent,
ProjectSelector,
@@ -24,7 +24,6 @@ import type {
ChecklistRequest,
ChecklistResultResponse,
ChecklistScriptureRange,
- IVersificationService,
} from 'platform-scripture';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ChecklistTool, CHECKLIST_STRING_KEYS } from './components/checklist.component';
@@ -432,25 +431,23 @@ global.webViewComponent = function ChecklistWebView({
// ─── Versification lookups (Theme 6) ──────────────────────────────────────
//
- // Mirrors platform-scripture-editor.web-view.tsx:351-377. Uses VersificationService for
- // current-book verse counts; other books would need their own fetch/cache (matches the
- // scripture-editor's existing limitation).
+ // Mirrors the versification-PDP block in platform-scripture-editor.web-view.tsx. Uses the
+ // per-project Versification PDP for current-book verse counts; other books would need their own
+ // fetch/cache (matches the scripture-editor's existing limitation).
const currentBookNum = useMemo(() => Canon.bookIdToNumber(liveScrRef.book), [liveScrRef.book]);
+ const versificationPdp = useProjectDataProvider('platformScripture.Versification', projectId);
+
const fetchLastVersesInCurrentBook = useCallback(async (): Promise => {
- if (!projectId || currentBookNum <= 0) return undefined;
+ if (!versificationPdp || currentBookNum <= 0) return undefined;
try {
- const versificationService = await papi.networkObjects.get(
- 'platformScripture.versificationService',
- );
- if (!versificationService) return undefined;
- return await versificationService.lookupFinalVerseNumbersInBook(projectId, currentBookNum);
+ return await versificationPdp.lookupFinalVerseNumbersInBook(currentBookNum);
} catch (err) {
- logger.debug(`ChecklistWebView: VersificationService unavailable: ${getErrorMessage(err)}`);
+ logger.debug(`ChecklistWebView: Versification PDP unavailable: ${getErrorMessage(err)}`);
return undefined;
}
- }, [projectId, currentBookNum]);
+ }, [versificationPdp, currentBookNum]);
const [lastVersesInCurrentBook] = usePromise(fetchLastVersesInCurrentBook, undefined);
const getEndVerse = useCallback(
@@ -462,10 +459,10 @@ global.webViewComponent = function ChecklistWebView({
);
// Last-chapter lookup derived from the same per-book array as getEndVerse.
- // The verses array is 1-indexed (matches scripture-editor.web-view.tsx:374's
- // `[chapterNum]` access pattern), so length - 1 yields the highest chapter number.
- // Returns 0 for non-current books — computeRangeFromScope tolerates 0 by falling back
- // to the documented 999 sentinel (FALLBACK_END_CHAPTER).
+ // The verses array is 1-indexed (matches the `[chapterNum]` access in getEndVerse above), so
+ // length - 1 yields the highest chapter number. Returns 0 for non-current books —
+ // computeRangeFromScope tolerates 0 by falling back to the documented 999 sentinel
+ // (FALLBACK_END_CHAPTER).
const getLastChapter = useCallback(
(bookId: string): number => {
if (Canon.bookIdToNumber(bookId) !== currentBookNum) return 0;
diff --git a/extensions/src/platform-scripture/src/types/platform-scripture.d.ts b/extensions/src/platform-scripture/src/types/platform-scripture.d.ts
index 2fc3cb74f61..fc712fed8d5 100644
--- a/extensions/src/platform-scripture/src/types/platform-scripture.d.ts
+++ b/extensions/src/platform-scripture/src/types/platform-scripture.d.ts
@@ -802,29 +802,55 @@ declare module 'platform-scripture' {
// #region Versification Types
/**
- * Read-only lookups for a project's versification — final chapter per book, final verse per
- * chapter. Consumers (e.g. reference pickers) use these to constrain selection to valid
- * references for a given project. This is a network object (not a project data provider):
- * versification is fixed at project open and does not change at runtime, so there is no
- * subscription semantics.
+ * Data types the versification projectInterface exposes via its base {@link IProjectDataProvider}
+ * — currently empty. The interface is auxiliary-only (no `get*` / `set*` / `subscribe*` data
+ * types). Consumers that need to react to versification changes should subscribe to the
+ * `'platformScripture.versification'` project setting on the same PDP via the base
+ * setting-subscription methods.
+ */
+ export type VersificationProjectInterfaceDataTypes = Record;
+
+ /**
+ * Project-scoped read-only versification lookups — final chapter per book, final verse per
+ * chapter. Consumers (e.g. reference pickers, range validators) use these to constrain selection
+ * to valid references for the project.
*
- * Obtain via
- * `papi.networkObjects.get('platformScripture.versificationService')`.
+ * Each call reads the project's _current_ versification fresh, so results reflect any in-session
+ * changes to the `platformScripture.versification` project setting. Consumers that cache results
+ * across calls should invalidate their cache when that setting changes.
+ *
+ * Acquire via `papi.projectDataProviders.get('platformScripture.Versification', projectId)`
+ * (backend) or the `useProjectDataProvider('platformScripture.Versification', projectId)` React
+ * hook (frontend).
*/
- export type IVersificationService = {
- /**
- * Returns the final verse number in the specified book and chapter using the project's
- * versification.
- */
- lookupFinalVerseNumber(projectId: string, bookNum: number, chapterNum: number): Promise;
- /** Returns the final chapter number in the specified book using the project's versification. */
- lookupFinalChapter(projectId: string, bookNum: number): Promise;
- /**
- * Returns an array where index `n` is the last verse number in chapter `n` (1-based). Index 0
- * is unused. Useful for pre-fetching all verse counts for a book in a single round trip.
- */
- lookupFinalVerseNumbersInBook(projectId: string, bookNum: number): Promise;
- };
+ export type IVersificationProjectDataProvider =
+ IProjectDataProvider & {
+ /**
+ * Returns the final verse number in the specified book and chapter using the project's
+ * versification.
+ */
+ lookupFinalVerseNumber(bookNum: number, chapterNum: number): Promise;
+ /** Returns the final chapter number in the specified book using the project's versification. */
+ lookupFinalChapter(bookNum: number): Promise;
+ /**
+ * Returns an array of final verse numbers for every chapter in a book, indexed 1-based for
+ * ergonomic `result[chapterNum]` access (no off-by-one). Index 0 is a filler `0` — it is not
+ * a valid chapter. The returned array has length `lastChapter + 1`.
+ *
+ * Useful for pre-fetching all verse counts for a book in a single round trip — preferable to
+ * `lookupFinalVerseNumber` in a loop when the caller needs many chapters of the same book.
+ *
+ * @example
+ *
+ * ```typescript
+ * const finalVerses = await pdp.lookupFinalVerseNumbersInBook(1); // Genesis
+ * finalVerses[1]; // → 31 (last verse of Genesis 1)
+ * finalVerses[50]; // → 26 (last verse of Genesis 50)
+ * finalVerses[0]; // → 0 (filler; chapter 0 does not exist)
+ * ```
+ */
+ lookupFinalVerseNumbersInBook(bookNum: number): Promise;
+ };
// #endregion Versification Types
@@ -1728,6 +1754,7 @@ declare module 'papi-shared-types' {
IUSJVerseProjectDataProvider,
IPlainTextVerseProjectDataProvider,
IMarkerNamesProjectDataProvider,
+ IVersificationProjectDataProvider,
IFindInScriptureProjectDataProvider,
IReplaceWithUsfmProjectDataProvider,
ICheckAggregatorService,
@@ -1750,6 +1777,7 @@ declare module 'papi-shared-types' {
'platformScripture.USJ_Verse': IUSJVerseProjectDataProvider;
'platformScripture.PlainText_Verse': IPlainTextVerseProjectDataProvider;
'platformScripture.MarkerNames': IMarkerNamesProjectDataProvider;
+ 'platformScripture.Versification': IVersificationProjectDataProvider;
'platformScripture.findInScripture': IFindInScriptureProjectDataProvider;
'platformScripture.replaceWithUsfm': IReplaceWithUsfmProjectDataProvider;
}