@devilsdev/rag-core 3.5.0 → 3.6.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.
package/dist/all.d.ts CHANGED
@@ -531,6 +531,7 @@ export { LocalEmbedder } from "./types/connectors/local-embedder";
531
531
  export { MemoryRetriever } from "./types/connectors/memory-retriever";
532
532
  export { PgVectorRetriever } from "./types/retrieval/pgvector-retriever";
533
533
  export { QdrantRetriever } from "./types/retrieval/qdrant-retriever";
534
+ export { PineconeRetriever } from "./types/retrieval/pinecone-retriever";
534
535
  export { OpenAIConnector } from "./types/connectors/openai-connector";
535
536
  export { AnthropicConnector } from "./types/connectors/anthropic-connector";
536
537
  export { CohereConnector } from "./types/connectors/cohere-connector";
package/dist/index.cjs CHANGED
@@ -1358,7 +1358,7 @@ var require_package = __commonJS({
1358
1358
  "package.json"(exports2, module2) {
1359
1359
  module2.exports = {
1360
1360
  name: "@devilsdev/rag-pipeline-utils",
1361
- version: "3.5.0",
1361
+ version: "3.6.0",
1362
1362
  description: "Composable retrieval-augmented generation (RAG) for Node.js: grounded citations, evaluation, guardrails, hybrid retrieval and a CLI.",
1363
1363
  main: "dist/index.cjs",
1364
1364
  type: "commonjs",
@@ -1376,6 +1376,7 @@ var require_package = __commonJS({
1376
1376
  exports: {
1377
1377
  ".": {
1378
1378
  types: "./dist/index.d.ts",
1379
+ workerd: "./dist/index.cjs",
1379
1380
  require: "./dist/index.cjs",
1380
1381
  import: "./dist/index.mjs"
1381
1382
  },
@@ -1404,6 +1405,8 @@ var require_package = __commonJS({
1404
1405
  scripts: {
1405
1406
  build: "node scripts/build-packages.js --bundles && node scripts/build.js && node scripts/generate-types.js && node scripts/build-packages.js --types && node scripts/check-type-exports.js",
1406
1407
  "test:packages": "node scripts/test-packages.js",
1408
+ "test:consumer": "node scripts/consumer-journeys.js",
1409
+ "test:templates": "node scripts/test-templates.js",
1407
1410
  prepack: "npm run build && node scripts/pin-readme-assets.js",
1408
1411
  postpack: "node scripts/pin-readme-assets.js --restore",
1409
1412
  "docs:validate": "node scripts/validate-docs-examples.js",
@@ -1459,6 +1462,7 @@ var require_package = __commonJS({
1459
1462
  "bench:claude-citations": "node benchmarks/claude-citations.js",
1460
1463
  "bench:judge-more": "node benchmarks/judge-more.js",
1461
1464
  "bench:claude-latency": "node benchmarks/claude-latency.js",
1465
+ "claude:usage": "node scripts/claude-usage-report.js && node benchmarks/render.js",
1462
1466
  dev: "node scripts/dev-tools.js menu",
1463
1467
  "dev:health": "node scripts/dev-tools.js health",
1464
1468
  "dev:setup": "node scripts/dev-tools.js setup",
@@ -1622,10 +1626,10 @@ var require_package = __commonJS({
1622
1626
  "why-is-node-running": "^3.2.2"
1623
1627
  },
1624
1628
  dependencies: {
1625
- "@devilsdev/rag-connectors": "3.5.0",
1626
- "@devilsdev/rag-core": "3.5.0",
1627
- "@devilsdev/rag-eval": "3.5.0",
1628
- "@devilsdev/rag-guardrails": "3.5.0",
1629
+ "@devilsdev/rag-connectors": "3.6.0",
1630
+ "@devilsdev/rag-core": "3.6.0",
1631
+ "@devilsdev/rag-eval": "3.6.0",
1632
+ "@devilsdev/rag-guardrails": "3.6.0",
1629
1633
  ajv: "^8.17.1",
1630
1634
  axios: "^1.21.0",
1631
1635
  chokidar: "^4.0.3",
@@ -1695,12 +1699,18 @@ var require_optional_peer = __commonJS({
1695
1699
  throw error;
1696
1700
  }
1697
1701
  }
1702
+ function missingPeer(error, name, hint) {
1703
+ if (!isMissing(error, name)) return error;
1704
+ const err = new Error(hint);
1705
+ err.code = "MISSING_DEPENDENCY";
1706
+ return err;
1707
+ }
1698
1708
  function isMissing(error, name) {
1699
1709
  if (!error || error.code !== "MODULE_NOT_FOUND") return false;
1700
1710
  const message = String(error.message || "");
1701
1711
  return message.startsWith(`Cannot find module '${name}'`) || message.startsWith(`Cannot find module "${name}"`);
1702
1712
  }
1703
- module2.exports = { loadOptionalPeer };
1713
+ module2.exports = { loadOptionalPeer, missingPeer };
1704
1714
  }
1705
1715
  });
1706
1716
 
@@ -1710,16 +1720,19 @@ var require_otel = __commonJS({
1710
1720
  "use strict";
1711
1721
  var { evaluationScores } = require_scores();
1712
1722
  var { version: LIBRARY_VERSION } = require_package();
1713
- var { loadOptionalPeer } = require_optional_peer();
1723
+ var { missingPeer } = require_optional_peer();
1714
1724
  var TRACER_NAME = "@devilsdev/rag-pipeline-utils";
1715
1725
  var PROVIDERS = /* @__PURE__ */ new Set(["openai", "anthropic", "cohere", "ollama"]);
1716
1726
  function loadOtelApi() {
1717
- return loadOptionalPeer(
1718
- "@opentelemetry/api",
1719
- () => require("@opentelemetry/api"),
1720
- // eslint-disable-line global-require
1721
- 'telemetry needs the "@opentelemetry/api" package, an optional peer dependency: npm install @opentelemetry/api (with @devilsdev/rag-pipeline-utils/edge, pass it: telemetry: { api })'
1722
- );
1727
+ try {
1728
+ return require("@opentelemetry/api");
1729
+ } catch (error) {
1730
+ throw missingPeer(
1731
+ error,
1732
+ "@opentelemetry/api",
1733
+ 'telemetry needs the "@opentelemetry/api" package, an optional peer dependency: npm install @opentelemetry/api (with @devilsdev/rag-pipeline-utils/edge, pass it: telemetry: { api })'
1734
+ );
1735
+ }
1723
1736
  }
1724
1737
  function createTelemetry(option) {
1725
1738
  if (!option) return null;
@@ -2584,17 +2597,114 @@ var require_scoring = __commonJS({
2584
2597
  return { values, parts: [...values, ...parts] };
2585
2598
  }
2586
2599
  function checkNumericConsistency(claimText, docText) {
2587
- const claimNumbers = [...new Set(extractNumbers(claimText).values)];
2600
+ const claimNumbers = [
2601
+ ...new Set(extractNumbers(withoutReferences(claimText)).values),
2602
+ ...claimMonths(claimText).map((m) => `month:${m}`),
2603
+ ...identifiers(claimText)
2604
+ ];
2588
2605
  if (claimNumbers.length === 0) {
2589
2606
  return { consistent: true, claimNumbers, missing: [] };
2590
2607
  }
2591
- const docNumbers = new Set(extractNumbers(docText).parts);
2608
+ const docNumbers = /* @__PURE__ */ new Set([
2609
+ ...extractNumbers(docText).parts,
2610
+ ...docMonths(docText).map((m) => `month:${m}`),
2611
+ ...identifiers(docText)
2612
+ ]);
2592
2613
  const missing = claimNumbers.filter((n) => !docNumbers.has(n));
2593
2614
  return { consistent: missing.length === 0, claimNumbers, missing };
2594
2615
  }
2616
+ var REFERENCE_WORDS = "document|documents|doc|docs|source|sources|table|tables|figure|figures|fig|section|sections|step|steps|study|studies|appendix|chapter|page|pages|claim|claims|passage|passages|item|items|reference|ref|part|exhibit|equation|eq|line|row|column|note|footnote|paragraph";
2617
+ var REFERENCE_RE = new RegExp(
2618
+ `\\b(${REFERENCE_WORDS})\\.?\\s+\\d+[a-z]?\\b`,
2619
+ "giu"
2620
+ );
2621
+ var IDENTIFIER_RE = new RegExp("(\\p{L}[\\p{L}\\p{N}]*)-(\\d+)\\b", "gu");
2622
+ function identifiers(text) {
2623
+ if (!text || typeof text !== "string") return [];
2624
+ return [
2625
+ ...new Set(
2626
+ [...text.matchAll(IDENTIFIER_RE)].map((m) => `id:${m[0].toLowerCase()}`)
2627
+ )
2628
+ ];
2629
+ }
2630
+ function withoutReferences(text) {
2631
+ if (!text || typeof text !== "string") return text;
2632
+ return text.replace(REFERENCE_RE, " ").replace(IDENTIFIER_RE, (_, word) => word);
2633
+ }
2634
+ var MONTHS = [
2635
+ ["january", "jan"],
2636
+ ["february", "feb"],
2637
+ ["march", "mar"],
2638
+ ["april", "apr"],
2639
+ ["may"],
2640
+ ["june", "jun"],
2641
+ ["july", "jul"],
2642
+ ["august", "aug"],
2643
+ ["september", "sep", "sept"],
2644
+ ["october", "oct"],
2645
+ ["november", "nov"],
2646
+ ["december", "dec"]
2647
+ ];
2648
+ var MONTH_OF = new Map(
2649
+ MONTHS.flatMap((names, i) => names.map((n) => [n, i + 1]))
2650
+ );
2651
+ function claimMonths(text) {
2652
+ const words = String(text || "").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
2653
+ const out = [];
2654
+ words.forEach((w, i) => {
2655
+ const m = MONTH_OF.get(w);
2656
+ if (!m) return;
2657
+ const near = [words[i - 1], words[i + 1]].some(
2658
+ (x) => /^\d+$/.test(x || "")
2659
+ );
2660
+ if (near) out.push(m);
2661
+ });
2662
+ return [...new Set(out)];
2663
+ }
2664
+ function docMonths(text) {
2665
+ const t = String(text || "");
2666
+ const out = /* @__PURE__ */ new Set();
2667
+ for (const w of t.toLowerCase().split(/[^\p{L}]+/u)) {
2668
+ const m = MONTH_OF.get(w);
2669
+ if (m) out.add(m);
2670
+ }
2671
+ for (const [, a, b] of t.matchAll(
2672
+ /\b(\d{1,2})[/.-](\d{1,2})[/.-]\d{2,4}\b/g
2673
+ )) {
2674
+ for (const v of [Number(a), Number(b)]) if (v >= 1 && v <= 12) out.add(v);
2675
+ }
2676
+ for (const [, mm] of t.matchAll(/\b\d{4}-(\d{1,2})-\d{1,2}\b/g)) {
2677
+ const v = Number(mm);
2678
+ if (v >= 1 && v <= 12) out.add(v);
2679
+ }
2680
+ return [...out];
2681
+ }
2595
2682
  var NEGATION_WORDS = new Set(
2596
- "not no never none nobody nothing nowhere neither nor cannot without dont doesnt didnt isnt arent wasnt werent wont wouldnt shouldnt couldnt cant hasnt havent hadnt mustnt neednt".split(" ")
2683
+ "not no never none nobody nothing nowhere neither nor cannot without dont doesnt didnt isnt arent wasnt werent wont wouldnt shouldnt couldnt cant hasnt havent hadnt mustnt neednt nicht kein keine keinen keinem keiner keines nie niemals nunca jam\xE1s jamas nadie nada tampoco ni pas jamais rien aucun aucune".split(" ")
2684
+ );
2685
+ var CLAUSE_BOUNDARY_WORDS = new Set(
2686
+ "but whereas although though while however yet and or except unlike".split(
2687
+ " "
2688
+ )
2597
2689
  );
2690
+ function clauseTokens(text) {
2691
+ const out = [];
2692
+ for (const part of String(text || "").split(/[,;:()[\]]+/)) {
2693
+ let current = [];
2694
+ for (const t of tokenize(part)) {
2695
+ if (CLAUSE_BOUNDARY_WORDS.has(t)) {
2696
+ if (current.length) out.push(current);
2697
+ current = [];
2698
+ } else {
2699
+ current.push(t);
2700
+ }
2701
+ }
2702
+ if (current.length) out.push(current);
2703
+ }
2704
+ return out;
2705
+ }
2706
+ var stem = (t) => t.length > 4 ? t.replace(/(ing|ed|es|s|e)$/, "") : t;
2707
+ var CONTINUING_NEGATIONS = /* @__PURE__ */ new Set(["nor", "ni"]);
2598
2708
  var NON_NEGATING_NEXT = {
2599
2709
  not: /* @__PURE__ */ new Set(["only", "just", "merely"]),
2600
2710
  no: /* @__PURE__ */ new Set(["doubt", "matter"]),
@@ -2605,6 +2715,7 @@ var require_scoring = __commonJS({
2605
2715
  for (let i = 0; i < tokens.length; i++) {
2606
2716
  const t = tokens[i];
2607
2717
  if (!NEGATION_WORDS.has(t)) continue;
2718
+ if (CONTINUING_NEGATIONS.has(t) && count > 0) continue;
2608
2719
  if (NON_NEGATING_NEXT[t] && NON_NEGATING_NEXT[t].has(tokens[i + 1])) {
2609
2720
  continue;
2610
2721
  }
@@ -2613,38 +2724,62 @@ var require_scoring = __commonJS({
2613
2724
  return count % 2 === 1;
2614
2725
  }
2615
2726
  function checkPolarityConsistency(claimText, docText) {
2616
- const claimTokens = tokenize(claimText);
2617
- const claimNegated = isNegated(claimTokens);
2618
- const claimWords = [
2727
+ const words = (tokens) => [
2619
2728
  ...new Set(
2620
- contentTokens(claimTokens.filter((t) => !NEGATION_WORDS.has(t)))
2729
+ contentTokens(tokens.filter((t) => !NEGATION_WORDS.has(t))).map(stem)
2621
2730
  )
2622
2731
  ];
2623
- if (claimWords.length === 0) {
2624
- return { consistent: true, claimNegated, sourceSentence: null };
2625
- }
2626
- const candidates = splitSentences(docText).map((sentence) => {
2627
- const tokens = tokenize(sentence);
2628
- const set = new Set(tokens);
2629
- const support = claimWords.filter((t) => set.has(t)).length / claimWords.length;
2630
- return { sentence, tokens, support };
2631
- }).filter((c) => c.support > 0).sort((a, b) => b.support - a.support);
2632
- if (candidates.length === 0) {
2732
+ const claimClauses = clauseTokens(claimText).map((tokens) => ({ tokens, words: words(tokens) })).filter((c) => c.words.length > 0);
2733
+ const claimNegated = claimClauses.some((c) => isNegated(c.tokens));
2734
+ if (claimClauses.length === 0) {
2633
2735
  return { consistent: true, claimNegated, sourceSentence: null };
2634
2736
  }
2635
- const best = candidates[0].support;
2636
- const compared = candidates.filter((c) => c.support === best);
2637
- const match = compared.find((c) => isNegated(c.tokens) === claimNegated);
2638
- return {
2639
- consistent: Boolean(match),
2640
- claimNegated,
2641
- sourceSentence: (match || compared[0]).sentence
2737
+ const sentences = splitSentences(docText).map((sentence) => {
2738
+ const clauses = clauseTokens(sentence).map((tokens) => ({
2739
+ negated: isNegated(tokens),
2740
+ stems: new Set(tokens.map(stem))
2741
+ }));
2742
+ const stems = new Set(clauses.flatMap((c) => [...c.stems]));
2743
+ return { sentence, clauses, stems };
2744
+ });
2745
+ const agrees = (clause, sentence) => {
2746
+ const negated = isNegated(clause.tokens);
2747
+ const distinctive = clause.words.map((w) => sentence.clauses.filter((c) => c.stems.has(w))).filter((owners) => owners.length === 1).map((owners) => owners[0]);
2748
+ if (distinctive.length > 0) {
2749
+ return distinctive.every((c) => c.negated === negated);
2750
+ }
2751
+ const ranked = sentence.clauses.map((c) => ({
2752
+ c,
2753
+ support: clause.words.filter((w) => c.stems.has(w)).length
2754
+ })).filter((r) => r.support > 0).sort((a, b) => b.support - a.support);
2755
+ if (ranked.length === 0) return true;
2756
+ const best = ranked[0].support;
2757
+ return ranked.filter((r) => r.support === best).some((r) => r.c.negated === negated);
2642
2758
  };
2759
+ let consistent = true;
2760
+ let sourceSentence = null;
2761
+ for (const clause of claimClauses) {
2762
+ const candidates = sentences.map((s) => ({
2763
+ ...s,
2764
+ support: clause.words.filter((w) => s.stems.has(w)).length / clause.words.length
2765
+ })).filter((s) => s.support > 0).sort((a, b) => b.support - a.support);
2766
+ if (candidates.length === 0) continue;
2767
+ const best = candidates[0].support;
2768
+ const compared = candidates.filter((s) => s.support === best);
2769
+ const match = compared.find((s) => agrees(clause, s));
2770
+ if (!match) consistent = false;
2771
+ if (sourceSentence === null || !match) {
2772
+ sourceSentence = (match || compared[0]).sentence;
2773
+ }
2774
+ }
2775
+ return { consistent, claimNegated, sourceSentence };
2643
2776
  }
2644
2777
  var POLARITY_MISMATCH_FACTOR = 0.4;
2645
2778
  var NUMERIC_MISMATCH_FACTOR = 0.4;
2646
2779
  function computeSupportScore(claimTokens, docTokens, idfWeights = null, options = {}) {
2647
- const claim = [...new Set(contentTokens(claimTokens))];
2780
+ const rawClaim = options && options.claimText;
2781
+ const fromText = typeof rawClaim === "string" && rawClaim !== withoutReferences(rawClaim) ? tokenize(withoutReferences(rawClaim)) : claimTokens;
2782
+ const claim = [...new Set(contentTokens(fromText))];
2648
2783
  if (claim.length === 0) return 0;
2649
2784
  const docSet = docTokens instanceof Set ? docTokens : new Set(docTokens);
2650
2785
  let matched = 0;
@@ -42,6 +42,7 @@ export namespace BUILT_INS {
42
42
  memory: (cfg: any) => import("../connectors/memory-retriever").MemoryRetriever;
43
43
  pgvector: (cfg: any) => import("../retrieval/pgvector-retriever").PgVectorRetriever;
44
44
  qdrant: (cfg: any) => import("../retrieval/qdrant-retriever").QdrantRetriever;
45
+ pinecone: (cfg: any) => import("../retrieval/pinecone-retriever").PineconeRetriever;
45
46
  };
46
47
  namespace llm {
47
48
  export function openai_1(cfg: any): import("../connectors/openai-connector").OpenAIConnector;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Retriever on a Pinecone serverless index over its REST API, with no
3
+ * client package. The index must exist, with a dense vector of your
4
+ * embedding model's dimension; use the cosine metric for the scores the
5
+ * other retrievers return.
6
+ */
7
+ export class PineconeRetriever {
8
+ /**
9
+ * @param {Object} options
10
+ * @param {string} [options.apiKey] - Defaults to PINECONE_API_KEY
11
+ * @param {string} [options.index] - The index name, described on first use
12
+ * @param {string} [options.host] - The index host (from the console), instead of or with `index`
13
+ * @param {string} [options.namespace='rag_documents'] - A namespace this retriever owns
14
+ * @param {'auto'|'documents'|'vectors'} [options.api='auto'] - Document index (Documents API) or vector index; auto reads it from the index description
15
+ * @param {string} [options.vectorField] - The document index's dense_vector field (default: the schema's)
16
+ * @param {number} [options.freshnessTimeoutMs=30000] - How long a read waits for this retriever's writes to be indexed; 0 to not wait
17
+ * @param {string} [options.cacheKey] - Cache identity; change it when the index changes
18
+ * @param {Function} [options.fetch] - fetch implementation (defaults to the global)
19
+ */
20
+ constructor(options?: {
21
+ apiKey?: string;
22
+ index?: string;
23
+ host?: string;
24
+ namespace?: string;
25
+ api?: "auto" | "documents" | "vectors";
26
+ vectorField?: string;
27
+ freshnessTimeoutMs?: number;
28
+ cacheKey?: string;
29
+ fetch?: Function;
30
+ });
31
+ index: string;
32
+ namespace: string;
33
+ freshnessTimeoutMs: number;
34
+ cacheKey: string;
35
+ _apiKey: string;
36
+ _host: string;
37
+ _api: "auto" | "documents" | "vectors";
38
+ _vectorField: string;
39
+ _fetch: any;
40
+ _resolving: any;
41
+ _writeLsn: number;
42
+ _dimension: any;
43
+ /**
44
+ * Cache identity for createRagPipeline({ cache }): none unless you set
45
+ * `cacheKey`, since other clients can change the index.
46
+ * @param {'retriever'} role
47
+ * @returns {string|null}
48
+ */
49
+ getCacheKey(role: "retriever"): string | null;
50
+ /**
51
+ * One REST call; resolves the parsed body and the response headers.
52
+ * @private
53
+ */
54
+ private _call;
55
+ /**
56
+ * The index's host and kind, from its description (once): a document
57
+ * index's dense_vector field and dimension, or a vector index's.
58
+ * @private
59
+ */
60
+ private _resolve;
61
+ /** @private */
62
+ private _describe;
63
+ _indexDimension: any;
64
+ /** A data-plane call; records the log sequence number of writes. @private */
65
+ private _data;
66
+ /** The Documents API path for this namespace. @private */
67
+ private _docs;
68
+ /**
69
+ * A search, repeated until it reflects this retriever's writes (when the
70
+ * index reports log sequence numbers) or `freshnessTimeoutMs` passes.
71
+ * Resolves `[{ id, score, fields }]`.
72
+ * @private
73
+ */
74
+ private _search;
75
+ /** Waits until this retriever's writes are indexed (for list and size). @private */
76
+ private _waitForWrites;
77
+ /**
78
+ * Inserts or replaces documents.
79
+ * @param {Array<{ id: string, vector: number[], content?: string, metadata?: Object }>} docs
80
+ * @returns {Promise<void>}
81
+ */
82
+ store(docs: Array<{
83
+ id: string;
84
+ vector: number[];
85
+ content?: string;
86
+ metadata?: any;
87
+ }>): Promise<void>;
88
+ /**
89
+ * The documents most similar to a query vector.
90
+ * @param {number[]|{ queryVector?: number[], topK?: number, signal?: AbortSignal }} request
91
+ * A vector, or an object with one (required; optional in the type to
92
+ * match the retriever contract)
93
+ * @param {number} [k=10] - Results when `request` is a vector
94
+ * @returns {Promise<Array<{ id: string, score: number, content: string, metadata: Object }>>}
95
+ */
96
+ retrieve(request: number[] | {
97
+ queryVector?: number[];
98
+ topK?: number;
99
+ signal?: AbortSignal;
100
+ }, k?: number): Promise<Array<{
101
+ id: string;
102
+ score: number;
103
+ content: string;
104
+ metadata: any;
105
+ }>>;
106
+ /** @private */
107
+ private _delete;
108
+ /**
109
+ * Deletes the chunks of one source (`metadata.source`), for re-ingesting.
110
+ * @param {string} source
111
+ */
112
+ removeBySource(source: string): Promise<void>;
113
+ /**
114
+ * Deletes documents by id.
115
+ * @param {string[]} ids
116
+ */
117
+ delete(ids: string[]): Promise<void>;
118
+ /** Deletes every document this retriever stored in its namespace. */
119
+ clear(): Promise<void>;
120
+ /**
121
+ * Every document, without vectors.
122
+ * @param {{ limit?: number }} [options]
123
+ * @returns {Promise<Array<{ id: string, content: string, metadata: Object }>>}
124
+ */
125
+ list({ limit }?: {
126
+ limit?: number;
127
+ }): Promise<Array<{
128
+ id: string;
129
+ content: string;
130
+ metadata: any;
131
+ }>>;
132
+ /** @returns {Promise<number>} The documents this retriever stored */
133
+ size(): Promise<number>;
134
+ /** Documents API: fetch by filter, page by page. @private */
135
+ private _listDocuments;
136
+ /** Vectors API: list ids, then fetch them. @private */
137
+ private _listVectors;
138
+ }
139
+ /** The record id for a document id: itself when Pinecone accepts it. */
140
+ export function recordId(id: any): string;
141
+ export const API_VERSION: "2026-07";
@@ -2,7 +2,10 @@
2
2
  * Load an optional peer dependency. `load` must be a literal
3
3
  * `() => require('name')`, so bundlers (esbuild, Wrangler, webpack) can see
4
4
  * and include the package; probing with require.resolve() instead fails in
5
- * bundles even when the package is there.
5
+ * bundles even when the package is there. A bundle of a project without the
6
+ * package fails to build, though, because the require isn't inside a
7
+ * `try`: loaders on the main entry's path use `missingPeer` with the
8
+ * require in their own `try` instead.
6
9
  *
7
10
  * Only the package itself being absent becomes a `MISSING_DEPENDENCY` error
8
11
  * carrying `hint`; any other failure, including a module missing inside the
@@ -14,3 +17,20 @@
14
17
  * @returns {any} The module
15
18
  */
16
19
  export function loadOptionalPeer(name: string, load: () => any, hint: string): any;
20
+ /**
21
+ * For a loader that requires an optional peer inside its own `try`, which
22
+ * bundlers (esbuild, Wrangler) then include when the package is installed
23
+ * and leave out, without failing the build, when it isn't:
24
+ *
25
+ * try {
26
+ * return require('openai');
27
+ * } catch (error) {
28
+ * throw missingPeer(error, 'openai', hint);
29
+ * }
30
+ *
31
+ * @param {Error} error - What require() threw
32
+ * @param {string} name - The package name
33
+ * @param {string} hint - The error message when the package is missing
34
+ * @returns {Error} A `MISSING_DEPENDENCY` error carrying `hint` when the package itself is absent; otherwise `error`
35
+ */
36
+ export function missingPeer(error: Error, name: string, hint: string): Error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devilsdev/rag-core",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "createRagPipeline and its building blocks: retrieval, reranking, caching, cost tracking and model routing.",
5
5
  "keywords": [
6
6
  "rag",
@@ -26,6 +26,7 @@
26
26
  "exports": {
27
27
  ".": {
28
28
  "types": "./dist/index.d.ts",
29
+ "workerd": "./dist/index.cjs",
29
30
  "require": "./dist/index.cjs",
30
31
  "import": "./dist/index.mjs"
31
32
  },