@denizsvnc/ai-rag-engine 2.2.0 → 2.2.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/CHANGELOG.md CHANGED
@@ -1,8 +1,30 @@
1
1
  # Changelog
2
2
 
3
- This file follows Semantic Versioning. Entries are maintained by the repository owner. No npm publication or Git tag is implied by an entry.
3
+ This file follows Semantic Versioning.
4
4
 
5
- ## 2.2.0 — Unreleased
5
+ ## 2.2.2 — 2026-11-10
6
+
7
+ ### Fixed
8
+
9
+ - **Document Ingestion:** Resolved infinite decaying chunk loop in `DocumentIngestionService.chunkText` when `endIndex >= text.length`.
10
+ - **Security & Prompt Injection:** Resolved regex boundary and preposition issues in `InjectionDetector` for English DAN jailbreak attacks (`in DAN mode`) and Turkish Unicode word boundaries (`ş, ç`).
11
+ - **LLM Failover Logging:** Replaced generic `Object failed` error message in `FallbackLLMProvider` with dynamic provider, model, or constructor resolution.
12
+ - **Rules Adapter:** Extended `StaticRulesAdapter` to accept structured rule objects (`{ id, rule }`) alongside plain string arrays via polymorphic `RuleInput`.
13
+ - **Performance & SML Sharing:** Prevented redundant ONNX model downloads and duplicate memory footprint by sharing `SMLEngine` instances across `RAGEngine`, `LocalFaqAdapter`, and `MemoryVectorAdapter`.
14
+
15
+ ### Added
16
+
17
+ - Exported `JobStatusValues` constants in `job-queue.interface.ts` to prevent casing mismatches.
18
+ - Exported `RuleInput` type in root exports.
19
+
20
+ ## 2.2.1 — 2026-11-10
21
+
22
+ ### Documentation
23
+
24
+ - Updated `README.md`, `README_tr.md`, and `README_de.md` with verified, type-safe production examples featuring `GeminiAdapter`, `PostgresVectorAdapter`, `RedisCacheAdapter`, `LocalFaqAdapter`, and `SecurityConfig`.
25
+ - Removed legacy pre-publication disclaimers and streamlined installation documentation for live npm consumption.
26
+
27
+ ## 2.2.0 — 2026-11-10
6
28
 
7
29
  ### Added
8
30
 
package/README.md CHANGED
@@ -22,27 +22,14 @@
22
22
 
23
23
  </div>
24
24
 
25
- **Publication status:** prepared locally for `@denizsvnc/ai-rag-engine@2.2.0`; this is not a claim that it is already available on npm. Scope ownership and publication remain the maintainer's responsibility.
26
-
27
25
  ## Requirements and installation
28
26
 
29
27
  ESM only. Node.js **22.14 or newer**; the compatibility matrix covers Node 22 and 24. TypeScript consumers should use TypeScript 5.8+ with NodeNext or Bundler resolution. Runtime applications do not need TypeScript or build tools.
30
28
 
31
- After publication:
32
-
33
29
  ```sh
34
30
  npm install @denizsvnc/ai-rag-engine
35
31
  ```
36
32
 
37
- Before publication, install a locally built artifact:
38
-
39
- ```sh
40
- npm ci
41
- npm pack
42
- # From a different project:
43
- npm install /absolute/path/to/denizsvnc-ai-rag-engine-2.2.0.tgz
44
- ```
45
-
46
33
  ## Quick Start
47
34
 
48
35
  This deterministic example needs no credentials, model download, HTTP server, or database. The provider and vector adapter are test doubles demonstrating the integration contract; use real adapters for application data.
@@ -75,6 +62,71 @@ const result = await engine.query('What is the return window?', {
75
62
  console.log(result.answer); // Returns: 14 days.
76
63
  ```
77
64
 
65
+ ### Production Example (Multi-Tenant with Gemini, pgvector & Redis)
66
+
67
+ For production applications with external LLMs, multi-tenant persistence, and distributed caching:
68
+
69
+ ```ts
70
+ import {
71
+ RAGEngine,
72
+ GeminiAdapter,
73
+ PostgresVectorAdapter,
74
+ RedisCacheAdapter,
75
+ LocalFaqAdapter
76
+ } from '@denizsvnc/ai-rag-engine';
77
+
78
+ const engine = new RAGEngine({
79
+ // 1. LLM Provider (Gemini, OpenAI, Ollama, or Custom)
80
+ llmProvider: new GeminiAdapter(
81
+ process.env.GEMINI_API_KEY,
82
+ 'gemini-1.5-flash'
83
+ ),
84
+
85
+ // 2. Knowledge Retrieval & Semantic FAQ Matching
86
+ inputs: {
87
+ vectorStore: new PostgresVectorAdapter({
88
+ connectionString: process.env.DATABASE_URL,
89
+ tableName: 'enterprise_embeddings'
90
+ }),
91
+ faq: new LocalFaqAdapter({
92
+ filePath: './faq.json',
93
+ defaultMatchThreshold: 0.65
94
+ })
95
+ },
96
+
97
+ // 3. Distributed Cache & Security Guardrails
98
+ cache: {
99
+ enabled: true,
100
+ adapter: new RedisCacheAdapter({
101
+ url: process.env.REDIS_URL,
102
+ defaultTtlMs: 3600000 // 1 hour
103
+ })
104
+ },
105
+ security: {
106
+ enabled: true,
107
+ blockHighRiskInjections: true,
108
+ pii: {
109
+ enabled: true,
110
+ maskTCKN: true,
111
+ maskCreditCards: true,
112
+ maskEmails: true
113
+ }
114
+ }
115
+ });
116
+
117
+ // Query with tenant isolation & role-based access
118
+ const response = await engine.query('Q3 compliance audit summary', {
119
+ tenantId: 'tenant-acme',
120
+ userId: 'usr_8472',
121
+ roles: ['compliance_officer']
122
+ });
123
+
124
+ console.log(response.answer);
125
+ console.log('Intent:', response.intent);
126
+ console.log('Latency (ms):', response.latencyMs);
127
+ console.log('Sources:', response.sources);
128
+ ```
129
+
78
130
  Importing the SDK does not start a server, load `.env`, open Redis/PostgreSQL connections, download models, or change the Transformers global environment. Constructors and explicit initialization perform the operations described in [the usage guide](docs/USAGE.md).
79
131
 
80
132
  ## Capabilities and operational boundaries
@@ -96,7 +148,7 @@ Prompt filters and PII patterns are defenses with limitations, not guarantees of
96
148
  - [Configuration, real providers, vectors, FAQ, tools and troubleshooting](docs/USAGE.md)
97
149
  - [Versioning, manual publication and release checklist](docs/RELEASING.md)
98
150
  - [Changelog](CHANGELOG.md)
99
- - [Detailed local release-readiness report](https://github.com/Denizsvnc/ai-rag-engine/blob/main/docs/NPM_RELEASE_READINESS.md)
151
+ - [Detailed release-readiness report](docs/NPM_RELEASE_READINESS.md)
100
152
 
101
153
  ## Repository verification
102
154
 
@@ -16,6 +16,8 @@ export declare class LocalFaqAdapter implements IFAQAdapter {
16
16
  private items;
17
17
  private isLoaded;
18
18
  constructor(options?: LocalFaqAdapterOptions);
19
+ setSMLEngine(engine: SMLEngine): void;
20
+ getSMLEngine(): SMLEngine;
19
21
  /**
20
22
  * Initializes the adapter by reading the JSON file and caching embeddings in RAM.
21
23
  */
@@ -6,5 +6,6 @@ export declare class FallbackLLMProvider implements ILLMProvider {
6
6
  private providers;
7
7
  private logFailover;
8
8
  constructor(providers: ILLMProvider[], options?: FallbackProviderOptions);
9
+ private getProviderName;
9
10
  generate(request: ILLMRequest): Promise<ILLMResponse>;
10
11
  }
@@ -1,6 +1,11 @@
1
1
  import type { IRulesAdapter } from '../../interfaces/rules.interface.js';
2
+ export type RuleInput = string | {
3
+ id?: string;
4
+ rule: string;
5
+ [key: string]: any;
6
+ };
2
7
  export declare class StaticRulesAdapter implements IRulesAdapter {
3
8
  private rules;
4
- constructor(rules: string[]);
9
+ constructor(rules?: Array<RuleInput>);
5
10
  getActiveRules(tenantId?: string): Promise<string[]>;
6
11
  }
@@ -6,6 +6,8 @@ export declare class MemoryVectorAdapter implements IVectorStoreAdapter {
6
6
  private documents;
7
7
  private bm25Index;
8
8
  constructor(smlEngine?: SMLEngine);
9
+ setSMLEngine(engine: SMLEngine): void;
10
+ getSMLEngine(): SMLEngine;
9
11
  /**
10
12
  * Adds documents to memory, computes their embeddings, and indexes them in BM25.
11
13
  */
@@ -535,11 +535,11 @@ var InjectionDetector = class {
535
535
  // 2. Jailbreaks, DAN modes, role hijacking
536
536
  {
537
537
  id: "JAILBREAK_MODE_EN",
538
- regex: /\b(you\s+are\s+now|act\s+as|switch\s+to|enter)\s+(an?\s+)?(developer|dan|jailbreak|unrestricted|unfiltered|god)\s*(mode|persona|ai)?\b/i
538
+ regex: /\b(you\s+are\s+now|act\s+as|switch\s+to|enter)\s+(in\s+(an?\s+)?|an?\s+)?(developer|dan|jailbreak|unrestricted|unfiltered|god)\s*(mode|persona|ai)?\b/i
539
539
  },
540
540
  {
541
541
  id: "JAILBREAK_MODE_TR",
542
- regex: /\bartık\s+(geliştirici|dan|kısıtlamasız|admin|jailbreak)\s+(modundasın|moduna\s+geç|olarak\s+davran)\b/i
542
+ regex: /(?:^|\s|[.,!?])(artık|şimdi)\s+(bir\s+)?(geliştirici|dan|kısıtlamasız|admin|jailbreak)\s+(modundasın|moduna\s+geç|olarak\s+davran)(?:$|\s|[.,!?])/i
543
543
  },
544
544
  {
545
545
  id: "DISABLE_SAFETY_EN",
@@ -1565,6 +1565,9 @@ var DocumentIngestionService = class _DocumentIngestionService {
1565
1565
  if (chunk.length > 0) {
1566
1566
  chunks.push(chunk);
1567
1567
  }
1568
+ if (endIndex >= text.length) {
1569
+ break;
1570
+ }
1568
1571
  startIndex = Math.max(startIndex + 1, endIndex - overlap);
1569
1572
  }
1570
1573
  return chunks;
@@ -1945,6 +1948,12 @@ var MemoryVectorAdapter = class {
1945
1948
  smlEngine;
1946
1949
  documents = [];
1947
1950
  bm25Index = new BM25Index();
1951
+ setSMLEngine(engine) {
1952
+ this.smlEngine = engine;
1953
+ }
1954
+ getSMLEngine() {
1955
+ return this.smlEngine;
1956
+ }
1948
1957
  /**
1949
1958
  * Adds documents to memory, computes their embeddings, and indexes them in BM25.
1950
1959
  */
@@ -2046,6 +2055,12 @@ var RAGEngine = class {
2046
2055
  }
2047
2056
  this.smlEngine = new SMLEngine({ modelName: config.smlConfig?.modelName });
2048
2057
  const vectorStore = config.inputs?.vectorStore || new MemoryVectorAdapter(this.smlEngine);
2058
+ if (typeof vectorStore.setSMLEngine === "function") {
2059
+ vectorStore.setSMLEngine(this.smlEngine);
2060
+ }
2061
+ if (config.inputs?.faq && typeof config.inputs.faq.setSMLEngine === "function") {
2062
+ config.inputs.faq.setSMLEngine(this.smlEngine);
2063
+ }
2049
2064
  this.config = {
2050
2065
  ...config,
2051
2066
  inputs: {
@@ -2865,11 +2880,24 @@ var FallbackLLMProvider = class {
2865
2880
  this.providers = providers;
2866
2881
  this.logFailover = options?.logFailover ?? true;
2867
2882
  }
2883
+ getProviderName(provider, index) {
2884
+ if (provider.name && typeof provider.name === "string") {
2885
+ return provider.name;
2886
+ }
2887
+ if (provider.model && typeof provider.model === "string") {
2888
+ const ctor = provider.constructor?.name && provider.constructor.name !== "Object" ? provider.constructor.name : "LLMProvider";
2889
+ return `${ctor}(${provider.model})`;
2890
+ }
2891
+ if (provider.constructor?.name && provider.constructor.name !== "Object") {
2892
+ return provider.constructor.name;
2893
+ }
2894
+ return `Provider#${index + 1}`;
2895
+ }
2868
2896
  async generate(request) {
2869
2897
  const failureErrors = [];
2870
2898
  for (let i = 0; i < this.providers.length; i++) {
2871
2899
  const provider = this.providers[i];
2872
- const providerName = provider.constructor.name || `Provider#${i + 1}`;
2900
+ const providerName = this.getProviderName(provider, i);
2873
2901
  try {
2874
2902
  return await provider.generate(request);
2875
2903
  } catch (error) {
@@ -2877,7 +2905,7 @@ var FallbackLLMProvider = class {
2877
2905
  failureErrors.push({ providerName, error: errorMessage });
2878
2906
  if (this.logFailover) {
2879
2907
  const nextProvider = this.providers[i + 1];
2880
- const nextName = nextProvider ? nextProvider.constructor.name || `Provider#${i + 2}` : "None";
2908
+ const nextName = nextProvider ? this.getProviderName(nextProvider, i + 1) : "None";
2881
2909
  console.warn(
2882
2910
  `[FallbackLLMProvider] ${providerName} failed: "${errorMessage}". Failing over to next provider: ${nextName}`
2883
2911
  );
@@ -3006,8 +3034,12 @@ var TieredLLMProvider = class _TieredLLMProvider {
3006
3034
  // src/adapters/rules/static-rules.adapter.ts
3007
3035
  var StaticRulesAdapter = class {
3008
3036
  rules;
3009
- constructor(rules) {
3010
- this.rules = rules;
3037
+ constructor(rules = []) {
3038
+ this.rules = rules.map((r) => {
3039
+ if (typeof r === "string") return r;
3040
+ if (r && typeof r === "object" && typeof r.rule === "string") return r.rule;
3041
+ return String(r);
3042
+ });
3011
3043
  }
3012
3044
  // Optional tenantId can be used for tenant-specific rules
3013
3045
  async getActiveRules(tenantId) {
@@ -3029,6 +3061,12 @@ var LocalFaqAdapter = class {
3029
3061
  this.smlEngine = options?.smlEngine || new SMLEngine();
3030
3062
  this.defaultThreshold = options?.defaultMatchThreshold ?? 0.6;
3031
3063
  }
3064
+ setSMLEngine(engine) {
3065
+ this.smlEngine = engine;
3066
+ }
3067
+ getSMLEngine() {
3068
+ return this.smlEngine;
3069
+ }
3032
3070
  /**
3033
3071
  * Initializes the adapter by reading the JSON file and caching embeddings in RAM.
3034
3072
  */
@@ -1,4 +1,11 @@
1
1
  export type JobStatus = 'pending' | 'queued' | 'processing' | 'completed' | 'failed';
2
+ export declare const JobStatusValues: {
3
+ readonly PENDING: 'pending';
4
+ readonly QUEUED: 'queued';
5
+ readonly PROCESSING: 'processing';
6
+ readonly COMPLETED: 'completed';
7
+ readonly FAILED: 'failed';
8
+ };
2
9
  export interface Job<TData = any, TResult = any> {
3
10
  attemptsMade?: number | undefined;
4
11
  maxAttempts?: number | undefined;
package/dist/index.d.ts CHANGED
@@ -39,7 +39,7 @@ export { OllamaAdapter, type OllamaConfig } from './adapters/llm/ollama.adapter.
39
39
  export { FallbackLLMProvider, type FallbackProviderOptions } from './adapters/llm/fallback.adapter.js';
40
40
  export { TieredLLMProvider, type TieredLLMConfig } from './adapters/llm/tiered.adapter.js';
41
41
  export { MemoryVectorAdapter } from './adapters/vector/memory-vector.adapter.js';
42
- export { StaticRulesAdapter } from './adapters/rules/static-rules.adapter.js';
42
+ export { StaticRulesAdapter, type RuleInput } from './adapters/rules/static-rules.adapter.js';
43
43
  export { UrlFaqAdapter } from './adapters/faq/url-faq.adapter.js';
44
44
  export { LocalFaqAdapter, type LocalFaqAdapterOptions } from './adapters/faq/local-faq.adapter.js';
45
45
  export { RedisConfirmationNonceStore, MemoryConfirmationNonceStore, type ConfirmationNonceStore } from './core/security/nonce-store.js';
package/dist/index.js CHANGED
@@ -531,11 +531,11 @@ var InjectionDetector = class {
531
531
  // 2. Jailbreaks, DAN modes, role hijacking
532
532
  {
533
533
  id: "JAILBREAK_MODE_EN",
534
- regex: /\b(you\s+are\s+now|act\s+as|switch\s+to|enter)\s+(an?\s+)?(developer|dan|jailbreak|unrestricted|unfiltered|god)\s*(mode|persona|ai)?\b/i
534
+ regex: /\b(you\s+are\s+now|act\s+as|switch\s+to|enter)\s+(in\s+(an?\s+)?|an?\s+)?(developer|dan|jailbreak|unrestricted|unfiltered|god)\s*(mode|persona|ai)?\b/i
535
535
  },
536
536
  {
537
537
  id: "JAILBREAK_MODE_TR",
538
- regex: /\bartık\s+(geliştirici|dan|kısıtlamasız|admin|jailbreak)\s+(modundasın|moduna\s+geç|olarak\s+davran)\b/i
538
+ regex: /(?:^|\s|[.,!?])(artık|şimdi)\s+(bir\s+)?(geliştirici|dan|kısıtlamasız|admin|jailbreak)\s+(modundasın|moduna\s+geç|olarak\s+davran)(?:$|\s|[.,!?])/i
539
539
  },
540
540
  {
541
541
  id: "DISABLE_SAFETY_EN",
@@ -1561,6 +1561,9 @@ var DocumentIngestionService = class _DocumentIngestionService {
1561
1561
  if (chunk.length > 0) {
1562
1562
  chunks.push(chunk);
1563
1563
  }
1564
+ if (endIndex >= text.length) {
1565
+ break;
1566
+ }
1564
1567
  startIndex = Math.max(startIndex + 1, endIndex - overlap);
1565
1568
  }
1566
1569
  return chunks;
@@ -1941,6 +1944,12 @@ var MemoryVectorAdapter = class {
1941
1944
  smlEngine;
1942
1945
  documents = [];
1943
1946
  bm25Index = new BM25Index();
1947
+ setSMLEngine(engine) {
1948
+ this.smlEngine = engine;
1949
+ }
1950
+ getSMLEngine() {
1951
+ return this.smlEngine;
1952
+ }
1944
1953
  /**
1945
1954
  * Adds documents to memory, computes their embeddings, and indexes them in BM25.
1946
1955
  */
@@ -2042,6 +2051,12 @@ var RAGEngine = class {
2042
2051
  }
2043
2052
  this.smlEngine = new SMLEngine({ modelName: config.smlConfig?.modelName });
2044
2053
  const vectorStore = config.inputs?.vectorStore || new MemoryVectorAdapter(this.smlEngine);
2054
+ if (typeof vectorStore.setSMLEngine === "function") {
2055
+ vectorStore.setSMLEngine(this.smlEngine);
2056
+ }
2057
+ if (config.inputs?.faq && typeof config.inputs.faq.setSMLEngine === "function") {
2058
+ config.inputs.faq.setSMLEngine(this.smlEngine);
2059
+ }
2045
2060
  this.config = {
2046
2061
  ...config,
2047
2062
  inputs: {
@@ -2708,6 +2723,15 @@ var RAGEvaluator = class {
2708
2723
  }
2709
2724
  };
2710
2725
 
2726
+ // src/core/queue/job-queue.interface.ts
2727
+ var JobStatusValues = {
2728
+ PENDING: "pending",
2729
+ QUEUED: "queued",
2730
+ PROCESSING: "processing",
2731
+ COMPLETED: "completed",
2732
+ FAILED: "failed"
2733
+ };
2734
+
2711
2735
  // src/core/queue/bullmq-queue.ts
2712
2736
  import { Queue, Worker, UnrecoverableError } from "bullmq";
2713
2737
  import { Redis } from "ioredis";
@@ -3716,11 +3740,24 @@ var FallbackLLMProvider = class {
3716
3740
  this.providers = providers;
3717
3741
  this.logFailover = options?.logFailover ?? true;
3718
3742
  }
3743
+ getProviderName(provider, index) {
3744
+ if (provider.name && typeof provider.name === "string") {
3745
+ return provider.name;
3746
+ }
3747
+ if (provider.model && typeof provider.model === "string") {
3748
+ const ctor = provider.constructor?.name && provider.constructor.name !== "Object" ? provider.constructor.name : "LLMProvider";
3749
+ return `${ctor}(${provider.model})`;
3750
+ }
3751
+ if (provider.constructor?.name && provider.constructor.name !== "Object") {
3752
+ return provider.constructor.name;
3753
+ }
3754
+ return `Provider#${index + 1}`;
3755
+ }
3719
3756
  async generate(request) {
3720
3757
  const failureErrors = [];
3721
3758
  for (let i = 0; i < this.providers.length; i++) {
3722
3759
  const provider = this.providers[i];
3723
- const providerName = provider.constructor.name || `Provider#${i + 1}`;
3760
+ const providerName = this.getProviderName(provider, i);
3724
3761
  try {
3725
3762
  return await provider.generate(request);
3726
3763
  } catch (error) {
@@ -3728,7 +3765,7 @@ var FallbackLLMProvider = class {
3728
3765
  failureErrors.push({ providerName, error: errorMessage });
3729
3766
  if (this.logFailover) {
3730
3767
  const nextProvider = this.providers[i + 1];
3731
- const nextName = nextProvider ? nextProvider.constructor.name || `Provider#${i + 2}` : "None";
3768
+ const nextName = nextProvider ? this.getProviderName(nextProvider, i + 1) : "None";
3732
3769
  console.warn(
3733
3770
  `[FallbackLLMProvider] ${providerName} failed: "${errorMessage}". Failing over to next provider: ${nextName}`
3734
3771
  );
@@ -3857,8 +3894,12 @@ var TieredLLMProvider = class _TieredLLMProvider {
3857
3894
  // src/adapters/rules/static-rules.adapter.ts
3858
3895
  var StaticRulesAdapter = class {
3859
3896
  rules;
3860
- constructor(rules) {
3861
- this.rules = rules;
3897
+ constructor(rules = []) {
3898
+ this.rules = rules.map((r) => {
3899
+ if (typeof r === "string") return r;
3900
+ if (r && typeof r === "object" && typeof r.rule === "string") return r.rule;
3901
+ return String(r);
3902
+ });
3862
3903
  }
3863
3904
  // Optional tenantId can be used for tenant-specific rules
3864
3905
  async getActiveRules(tenantId) {
@@ -3915,6 +3956,12 @@ var LocalFaqAdapter = class {
3915
3956
  this.smlEngine = options?.smlEngine || new SMLEngine();
3916
3957
  this.defaultThreshold = options?.defaultMatchThreshold ?? 0.6;
3917
3958
  }
3959
+ setSMLEngine(engine) {
3960
+ this.smlEngine = engine;
3961
+ }
3962
+ getSMLEngine() {
3963
+ return this.smlEngine;
3964
+ }
3918
3965
  /**
3919
3966
  * Initializes the adapter by reading the JSON file and caching embeddings in RAM.
3920
3967
  */
@@ -4474,6 +4521,7 @@ export {
4474
4521
  IngestionWorker,
4475
4522
  InjectionDetector,
4476
4523
  InputSanitizer,
4524
+ JobStatusValues,
4477
4525
  LocalFaqAdapter,
4478
4526
  MemoryConfirmationNonceStore,
4479
4527
  MemoryVectorAdapter,
package/dist/worker.js CHANGED
@@ -1237,6 +1237,9 @@ var DocumentIngestionService = class _DocumentIngestionService {
1237
1237
  if (chunk.length > 0) {
1238
1238
  chunks.push(chunk);
1239
1239
  }
1240
+ if (endIndex >= text.length) {
1241
+ break;
1242
+ }
1240
1243
  startIndex = Math.max(startIndex + 1, endIndex - overlap);
1241
1244
  }
1242
1245
  return chunks;
package/docs/RELEASING.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Policy
4
4
 
5
- The recommended first public version is **2.2.0**: the repository already identifies its preceding API as 2.1.0, and the new distribution includes durable ingestion and persistence. No public npm history or tag was found during preparation. This recommendation does not assert that any version was published. The maintainer must approve the version before publication. If API stabilization is still desired, use a deliberate `2.2.0-rc.1` prerelease and a non-latest dist-tag.
5
+ The recommended first public version is **2.2.0**: the repository already identifies its preceding API as 2.1.0, and the new distribution includes durable ingestion and persistence. Version **2.2.0** is the official initial release published on npm under `@denizsvnc/ai-rag-engine`.
6
6
 
7
7
  After the first public release, use patch releases for compatible fixes, minor releases for compatible features, and major releases for incompatible public API, security-contract or supported-runtime changes. Document security-driven breaks and migration paths. Never reuse a published version; do not silently modify artifacts after review.
8
8
 
package/docs/USAGE.md CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  ## Runtime and module contract
4
4
 
5
- Install `@denizsvnc/ai-rag-engine` after publication, or its local `.tgz` before publication. Use ESM (`"type": "module"` in the application). CommonJS `require()` is not a supported contract; CommonJS applications can use asynchronous `import()`. Node.js 22.14+ is required; Node 22/24 are the explicit test matrix. TypeScript 5.8+ is recommended; NodeNext and Bundler resolution are validated with strict checking. `skipLibCheck` is not required. Supported exports are the root API and `./package.json`; internal `dist/` paths are private. CLI commands are separate entry points.
5
+ Install `@denizsvnc/ai-rag-engine` from npm:
6
+
7
+ ```sh
8
+ npm install @denizsvnc/ai-rag-engine
9
+ ```
10
+
11
+ Use ESM (`"type": "module"` in the application). CommonJS `require()` is not a supported contract; CommonJS applications can use asynchronous `import()`. Node.js 22.14+ is required; Node 22/24 are the explicit test matrix. TypeScript 5.8+ is recommended; NodeNext and Bundler resolution are validated with strict checking. `skipLibCheck` is not required. Supported exports are the root API and `./package.json`; internal `dist/` paths are private. CLI commands are separate entry points.
6
12
 
7
13
  The library never automatically loads `.env`. Applications may call `dotenv.config()` themselves. The server/worker/migration CLIs explicitly load `.env`. Importing the root library creates no server, connection, model download, or background worker. Native Transformers is imported lazily on the first embedding initialization.
8
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@denizsvnc/ai-rag-engine",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "TypeScript RAG engine with pluggable providers, local semantic routing, tenant-aware retrieval, and durable document ingestion.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",