@everdeep/pubmed 0.1.1 → 0.1.2
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/README.md +6 -4
- package/dist/index.cjs +7 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,12 +77,14 @@ Cursors are versioned, base64url-encoded, unsigned, implementation-specific cont
|
|
|
77
77
|
|
|
78
78
|
### Count changes during pagination
|
|
79
79
|
|
|
80
|
-
NCBI can return HTTP 200 with valid IDs but a different total on a later page. By default,
|
|
80
|
+
NCBI can return HTTP 200 with valid IDs but a different total on a later page. By default, `search()` and `searchAll()` **continue retrieving valid pages**, returning count-drift diagnostics and emitting a `search-total-drift` event. The default `totalDriftPolicy` is `"warn"` (version 0.1.1 defaulted to `"error"`); no explicit opt-in or event handler is required. This is best-effort pagination, not a snapshot guarantee.
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
To stop on count changes, explicitly configure `totalDriftPolicy: "error"`. Strict mode throws `PaginationConsistencyError` (`code: "PAGINATION_INCONSISTENT"`, `retryable: false`) before fetching the changed page's records. This is a consistency signal, **not provider unavailability**. The client does not automatically retry or restart such searches.
|
|
83
|
+
|
|
84
|
+
With the default policy:
|
|
83
85
|
|
|
84
86
|
```ts
|
|
85
|
-
const client = new PubMedClient({ email, tool
|
|
87
|
+
const client = new PubMedClient({ email, tool }); // totalDriftPolicy: "warn" by default
|
|
86
88
|
const first = await client.search({ query: "cancer", pageSize: 2 });
|
|
87
89
|
if (first.nextCursor) {
|
|
88
90
|
const second = await client.search({ cursor: first.nextCursor });
|
|
@@ -94,7 +96,7 @@ In both modes, `SearchBatch.total` and the pagination bound remain the **initial
|
|
|
94
96
|
|
|
95
97
|
Warn mode returns a `diagnostics` entry on each drifted page and emits a `search-total-drift` event. A consistency error exposes the same metadata through `.diagnostic` and `toJSON()`: `reason`, `originalTotal`, `observedTotal`, `offset`, `requestedIds`, and `returnedIds`. No queries, PMIDs, cursors, or history tokens are included. Record parse `warnings` remain separate.
|
|
96
98
|
|
|
97
|
-
Neither mode guarantees snapshot enumeration: equal counts do not prove stable membership or ordering, and page-local uniqueness does not detect cross-page duplicates or omissions. Warn mode
|
|
99
|
+
Neither mode guarantees snapshot enumeration: equal counts do not prove stable membership or ordering, and page-local uniqueness does not detect cross-page duplicates or omissions. Warn mode accepts that risk; it does not silently deduplicate or claim complete results. Consumers should persist by PMID idempotently and inspect `missingPmids` and `diagnostics`. Consumers requiring a stop on count drift should select `"error"`, but that alone does not establish exact enumeration.
|
|
98
100
|
|
|
99
101
|
For progressive consumption, use `searchAll()`. `maxResults` is required so a caller must make the retrieval bound explicit:
|
|
100
102
|
|
package/dist/index.cjs
CHANGED
|
@@ -1567,7 +1567,9 @@ var Transport = class {
|
|
|
1567
1567
|
const correlationId = (0, import_node_crypto.randomUUID)();
|
|
1568
1568
|
safeEvent(this.#onEvent, { type: "correlation-id", endpoint, correlationId });
|
|
1569
1569
|
try {
|
|
1570
|
-
|
|
1570
|
+
const value = await this.#coordinate(endpoint, parameters, decode, options, correlationId);
|
|
1571
|
+
if (options.signal?.aborted) throw new AbortedError();
|
|
1572
|
+
return value;
|
|
1571
1573
|
} catch (error) {
|
|
1572
1574
|
safeEvent(this.#onEvent, {
|
|
1573
1575
|
type: "terminal-failure",
|
|
@@ -1591,6 +1593,7 @@ ${semantic.toString()}`);
|
|
|
1591
1593
|
const cached = await this.#cache.read(key, signal);
|
|
1592
1594
|
if (cached.status === "hit") {
|
|
1593
1595
|
safeEvent(this.#onEvent, { type: "cache-hit", endpoint, correlationId });
|
|
1596
|
+
if (signal?.aborted) throw new AbortedError();
|
|
1594
1597
|
if (encoder.encode(cached.value).byteLength > this.#maxResponseBytes) {
|
|
1595
1598
|
await this.#cache.invalidate(key);
|
|
1596
1599
|
} else {
|
|
@@ -1902,7 +1905,7 @@ var PubMedClient = class {
|
|
|
1902
1905
|
if (options.totalDriftPolicy !== void 0 && options.totalDriftPolicy !== "error" && options.totalDriftPolicy !== "warn") {
|
|
1903
1906
|
throw new ValidationError('totalDriftPolicy must be "error" or "warn"');
|
|
1904
1907
|
}
|
|
1905
|
-
this.#totalDriftPolicy = options.totalDriftPolicy ?? "
|
|
1908
|
+
this.#totalDriftPolicy = options.totalDriftPolicy ?? "warn";
|
|
1906
1909
|
if (typeof options.email !== "string" || options.email.trim() === "") throw new ValidationError("email is required");
|
|
1907
1910
|
if (typeof options.tool !== "string" || options.tool.trim() === "") throw new ValidationError("tool is required");
|
|
1908
1911
|
if (options.apiKey !== void 0 && (typeof options.apiKey !== "string" || options.apiKey.trim() === "")) {
|
|
@@ -1966,6 +1969,7 @@ var PubMedClient = class {
|
|
|
1966
1969
|
let ordered = input.flatMap((pmid) => byPmid.get(pmid) ?? []);
|
|
1967
1970
|
if (unknown.length > 0) ordered = [...ordered, ...unknown];
|
|
1968
1971
|
if (options.includeLinkOuts === true && ordered.length > 0) ordered = await this.#enrich(ordered, options.signal);
|
|
1972
|
+
if (options.signal?.aborted) throw new AbortedError();
|
|
1969
1973
|
return {
|
|
1970
1974
|
records: ordered,
|
|
1971
1975
|
missingPmids: input.filter((pmid) => !byPmid.has(pmid)),
|
|
@@ -1994,6 +1998,7 @@ var PubMedClient = class {
|
|
|
1994
1998
|
);
|
|
1995
1999
|
received.push(...summaries);
|
|
1996
2000
|
}
|
|
2001
|
+
if (options.signal?.aborted) throw new AbortedError();
|
|
1997
2002
|
const byPmid = new Map(received.map((summary) => [summary.pmid, summary]));
|
|
1998
2003
|
return {
|
|
1999
2004
|
summaries: input.flatMap((pmid) => byPmid.get(pmid) ?? []),
|
package/dist/index.d.cts
CHANGED
|
@@ -408,7 +408,7 @@ interface PubMedClientOptions {
|
|
|
408
408
|
readonly maxResponseBytes?: number;
|
|
409
409
|
readonly maxQueuedRequests?: number;
|
|
410
410
|
readonly maxBatchSize?: number;
|
|
411
|
-
/** Default "
|
|
411
|
+
/** Default "warn": continue valid pages with drift diagnostics. "error" opts into stopping on count changes. Neither guarantees a snapshot. */
|
|
412
412
|
readonly totalDriftPolicy?: "error" | "warn";
|
|
413
413
|
}
|
|
414
414
|
|
package/dist/index.d.ts
CHANGED
|
@@ -408,7 +408,7 @@ interface PubMedClientOptions {
|
|
|
408
408
|
readonly maxResponseBytes?: number;
|
|
409
409
|
readonly maxQueuedRequests?: number;
|
|
410
410
|
readonly maxBatchSize?: number;
|
|
411
|
-
/** Default "
|
|
411
|
+
/** Default "warn": continue valid pages with drift diagnostics. "error" opts into stopping on count changes. Neither guarantees a snapshot. */
|
|
412
412
|
readonly totalDriftPolicy?: "error" | "warn";
|
|
413
413
|
}
|
|
414
414
|
|
package/dist/index.js
CHANGED
|
@@ -1522,7 +1522,9 @@ var Transport = class {
|
|
|
1522
1522
|
const correlationId = randomUUID();
|
|
1523
1523
|
safeEvent(this.#onEvent, { type: "correlation-id", endpoint, correlationId });
|
|
1524
1524
|
try {
|
|
1525
|
-
|
|
1525
|
+
const value = await this.#coordinate(endpoint, parameters, decode, options, correlationId);
|
|
1526
|
+
if (options.signal?.aborted) throw new AbortedError();
|
|
1527
|
+
return value;
|
|
1526
1528
|
} catch (error) {
|
|
1527
1529
|
safeEvent(this.#onEvent, {
|
|
1528
1530
|
type: "terminal-failure",
|
|
@@ -1546,6 +1548,7 @@ ${semantic.toString()}`);
|
|
|
1546
1548
|
const cached = await this.#cache.read(key, signal);
|
|
1547
1549
|
if (cached.status === "hit") {
|
|
1548
1550
|
safeEvent(this.#onEvent, { type: "cache-hit", endpoint, correlationId });
|
|
1551
|
+
if (signal?.aborted) throw new AbortedError();
|
|
1549
1552
|
if (encoder.encode(cached.value).byteLength > this.#maxResponseBytes) {
|
|
1550
1553
|
await this.#cache.invalidate(key);
|
|
1551
1554
|
} else {
|
|
@@ -1857,7 +1860,7 @@ var PubMedClient = class {
|
|
|
1857
1860
|
if (options.totalDriftPolicy !== void 0 && options.totalDriftPolicy !== "error" && options.totalDriftPolicy !== "warn") {
|
|
1858
1861
|
throw new ValidationError('totalDriftPolicy must be "error" or "warn"');
|
|
1859
1862
|
}
|
|
1860
|
-
this.#totalDriftPolicy = options.totalDriftPolicy ?? "
|
|
1863
|
+
this.#totalDriftPolicy = options.totalDriftPolicy ?? "warn";
|
|
1861
1864
|
if (typeof options.email !== "string" || options.email.trim() === "") throw new ValidationError("email is required");
|
|
1862
1865
|
if (typeof options.tool !== "string" || options.tool.trim() === "") throw new ValidationError("tool is required");
|
|
1863
1866
|
if (options.apiKey !== void 0 && (typeof options.apiKey !== "string" || options.apiKey.trim() === "")) {
|
|
@@ -1921,6 +1924,7 @@ var PubMedClient = class {
|
|
|
1921
1924
|
let ordered = input.flatMap((pmid) => byPmid.get(pmid) ?? []);
|
|
1922
1925
|
if (unknown.length > 0) ordered = [...ordered, ...unknown];
|
|
1923
1926
|
if (options.includeLinkOuts === true && ordered.length > 0) ordered = await this.#enrich(ordered, options.signal);
|
|
1927
|
+
if (options.signal?.aborted) throw new AbortedError();
|
|
1924
1928
|
return {
|
|
1925
1929
|
records: ordered,
|
|
1926
1930
|
missingPmids: input.filter((pmid) => !byPmid.has(pmid)),
|
|
@@ -1949,6 +1953,7 @@ var PubMedClient = class {
|
|
|
1949
1953
|
);
|
|
1950
1954
|
received.push(...summaries);
|
|
1951
1955
|
}
|
|
1956
|
+
if (options.signal?.aborted) throw new AbortedError();
|
|
1952
1957
|
const byPmid = new Map(received.map((summary) => [summary.pmid, summary]));
|
|
1953
1958
|
return {
|
|
1954
1959
|
summaries: input.flatMap((pmid) => byPmid.get(pmid) ?? []),
|