@waniwani/sdk 0.1.20-beta.26 → 0.1.20-beta.28

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.
@@ -1,5 +1,56 @@
1
1
  import { UIMessage } from 'ai';
2
2
 
3
+ interface SearchResult {
4
+ source: string;
5
+ heading: string;
6
+ content: string;
7
+ score: number;
8
+ }
9
+ /** A file to ingest into the knowledge base */
10
+ interface KbIngestFile {
11
+ /** Filename (used as chunk source identifier) */
12
+ filename: string;
13
+ /** Markdown content of the file */
14
+ content: string;
15
+ }
16
+ /** Response from the ingest endpoint */
17
+ interface KbIngestResult {
18
+ /** Number of chunks created from the ingested files */
19
+ chunksIngested: number;
20
+ /** Number of files successfully processed */
21
+ filesProcessed: number;
22
+ }
23
+ /** Options for the search method */
24
+ interface KbSearchOptions {
25
+ /** Number of results to return (1-20, default 5) */
26
+ topK?: number;
27
+ /** Minimum similarity score threshold (0-1, default 0.3) */
28
+ minScore?: number;
29
+ }
30
+ /** A source entry in the knowledge base */
31
+ interface KbSource {
32
+ /** Source filename */
33
+ source: string;
34
+ /** Number of chunks from this source */
35
+ chunkCount: number;
36
+ /** ISO timestamp of when the source was first ingested */
37
+ createdAt: string;
38
+ }
39
+ /** KB client for server-side knowledge base operations */
40
+ interface KbClient {
41
+ /**
42
+ * Ingest files into the knowledge base.
43
+ *
44
+ * **Warning**: This is destructive — it deletes ALL existing chunks
45
+ * for the environment before ingesting the new files.
46
+ */
47
+ ingest(files: KbIngestFile[]): Promise<KbIngestResult>;
48
+ /** Search the knowledge base for relevant chunks */
49
+ search(query: string, options?: KbSearchOptions): Promise<SearchResult[]>;
50
+ /** List all sources in the knowledge base */
51
+ sources(): Promise<KbSource[]>;
52
+ }
53
+
3
54
  type EventType = "session.started" | "tool.called" | "quote.requested" | "quote.succeeded" | "quote.failed" | "link.clicked" | "purchase.completed" | "widget_render" | "widget_click" | "widget_link_click" | "widget_error" | "widget_scroll" | "widget_form_field" | "widget_form_submit";
4
55
  interface ToolCalledProperties {
5
56
  name?: string;
@@ -140,6 +191,8 @@ interface TrackingClient {
140
191
  interface WaniWaniClient extends TrackingClient {
141
192
  /** @internal Resolved config — used by framework adapters */
142
193
  readonly _config: InternalConfig;
194
+ /** Knowledge base client for ingestion, search, and source listing */
195
+ readonly kb: KbClient;
143
196
  }
144
197
  interface InternalConfig {
145
198
  baseUrl: string;
package/dist/index.d.ts CHANGED
@@ -3,6 +3,57 @@ declare class WaniWaniError extends Error {
3
3
  constructor(message: string, status: number);
4
4
  }
5
5
 
6
+ interface SearchResult {
7
+ source: string;
8
+ heading: string;
9
+ content: string;
10
+ score: number;
11
+ }
12
+ /** A file to ingest into the knowledge base */
13
+ interface KbIngestFile {
14
+ /** Filename (used as chunk source identifier) */
15
+ filename: string;
16
+ /** Markdown content of the file */
17
+ content: string;
18
+ }
19
+ /** Response from the ingest endpoint */
20
+ interface KbIngestResult {
21
+ /** Number of chunks created from the ingested files */
22
+ chunksIngested: number;
23
+ /** Number of files successfully processed */
24
+ filesProcessed: number;
25
+ }
26
+ /** Options for the search method */
27
+ interface KbSearchOptions {
28
+ /** Number of results to return (1-20, default 5) */
29
+ topK?: number;
30
+ /** Minimum similarity score threshold (0-1, default 0.3) */
31
+ minScore?: number;
32
+ }
33
+ /** A source entry in the knowledge base */
34
+ interface KbSource {
35
+ /** Source filename */
36
+ source: string;
37
+ /** Number of chunks from this source */
38
+ chunkCount: number;
39
+ /** ISO timestamp of when the source was first ingested */
40
+ createdAt: string;
41
+ }
42
+ /** KB client for server-side knowledge base operations */
43
+ interface KbClient {
44
+ /**
45
+ * Ingest files into the knowledge base.
46
+ *
47
+ * **Warning**: This is destructive — it deletes ALL existing chunks
48
+ * for the environment before ingesting the new files.
49
+ */
50
+ ingest(files: KbIngestFile[]): Promise<KbIngestResult>;
51
+ /** Search the knowledge base for relevant chunks */
52
+ search(query: string, options?: KbSearchOptions): Promise<SearchResult[]>;
53
+ /** List all sources in the knowledge base */
54
+ sources(): Promise<KbSource[]>;
55
+ }
56
+
6
57
  type EventType = "session.started" | "tool.called" | "quote.requested" | "quote.succeeded" | "quote.failed" | "link.clicked" | "purchase.completed" | "widget_render" | "widget_click" | "widget_link_click" | "widget_error" | "widget_scroll" | "widget_form_field" | "widget_form_submit";
7
58
  interface ToolCalledProperties {
8
59
  name?: string;
@@ -164,6 +215,8 @@ interface WaniWaniConfig {
164
215
  interface WaniWaniClient extends TrackingClient {
165
216
  /** @internal Resolved config — used by framework adapters */
166
217
  readonly _config: InternalConfig;
218
+ /** Knowledge base client for ingestion, search, and source listing */
219
+ readonly kb: KbClient;
167
220
  }
168
221
  interface InternalConfig {
169
222
  baseUrl: string;
@@ -231,4 +284,4 @@ interface V2BatchResponse {
231
284
  */
232
285
  declare function waniwani(config?: WaniWaniConfig): WaniWaniClient;
233
286
 
234
- export { type EventType, type LegacyTrackEvent, type LinkClickedProperties, type PurchaseCompletedProperties, type QuoteSucceededProperties, type ToolCalledProperties, type TrackEvent, type TrackInput, type TrackingConfig, type TrackingShutdownOptions, type TrackingShutdownResult, type V2BatchRejectedEvent, type V2BatchRequest, type V2BatchResponse, type V2CorrelationIds, type V2EnvelopeType, type V2EventEnvelope, type WaniWaniClient, type WaniWaniConfig, WaniWaniError, waniwani };
287
+ export { type EventType, type KbClient, type KbIngestFile, type KbIngestResult, type KbSearchOptions, type KbSource, type LegacyTrackEvent, type LinkClickedProperties, type PurchaseCompletedProperties, type QuoteSucceededProperties, type ToolCalledProperties, type TrackEvent, type TrackInput, type TrackingConfig, type TrackingShutdownOptions, type TrackingShutdownResult, type V2BatchRejectedEvent, type V2BatchRequest, type V2BatchResponse, type V2CorrelationIds, type V2EnvelopeType, type V2EventEnvelope, type WaniWaniClient, type WaniWaniConfig, WaniWaniError, waniwani };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var g=class extends Error{constructor(n,r){super(n);this.status=r;this.name="WaniWaniError"}};var b="@waniwani/sdk";function y(e,t={}){let n=t.now??(()=>new Date),r=t.generateId??E,s=x(e),i=p(e.meta),o=p(e.metadata),a=D(e,i),h=u(e.eventId)??r(),w=B(e.timestamp,n),M=u(e.source)??t.source??b,f=v(e)?{...e}:void 0,m={...o};return Object.keys(i).length>0&&(m.meta=i),f&&(m.rawLegacy=f),{id:h,type:"mcp.event",name:s,source:M,timestamp:w,correlation:a,properties:R(e,s),metadata:m,rawLegacy:f}}function E(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?`evt_${crypto.randomUUID()}`:`evt_${Math.random().toString(36).slice(2,10)}_${Date.now().toString(36)}`}function R(e,t){if(!v(e))return p(e.properties);let n=_(e,t),r=p(e.properties);return{...n,...r}}function _(e,t){switch(t){case"tool.called":{let n={};return u(e.toolName)&&(n.name=e.toolName),u(e.toolType)&&(n.type=e.toolType),n}case"quote.succeeded":{let n={};return typeof e.quoteAmount=="number"&&(n.amount=e.quoteAmount),u(e.quoteCurrency)&&(n.currency=e.quoteCurrency),n}case"link.clicked":{let n={};return u(e.linkUrl)&&(n.url=e.linkUrl),n}case"purchase.completed":{let n={};return typeof e.purchaseAmount=="number"&&(n.amount=e.purchaseAmount),u(e.purchaseCurrency)&&(n.currency=e.purchaseCurrency),n}default:return{}}}function x(e){return v(e)?e.eventType:e.event}function D(e,t){let n=u(e.requestId)??l(t,["openai/requestId","requestId","mcp/requestId"]),r=u(e.sessionId)??l(t,["openai/sessionId","sessionId","conversationId","anthropic/sessionId"]),s=u(e.traceId)??l(t,["openai/traceId","traceId","mcp/traceId","openai/requestId","requestId"]),i=u(e.externalUserId)??l(t,["openai/userId","externalUserId","userId","actorId"]),o=u(e.correlationId)??l(t,["correlationId","openai/requestId"])??n,a={};return r&&(a.sessionId=r),s&&(a.traceId=s),n&&(a.requestId=n),o&&(a.correlationId=o),i&&(a.externalUserId=i),a}function B(e,t){if(e instanceof Date)return e.toISOString();if(typeof e=="string"){let n=new Date(e);if(!Number.isNaN(n.getTime()))return n.toISOString()}return t().toISOString()}function l(e,t){for(let n of t){let r=e[n];if(typeof r=="string"&&r.trim().length>0)return r}}function p(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function u(e){if(typeof e=="string"&&e.trim().length!==0)return e}function v(e){return"eventType"in e}var A="/api/mcp/events/v2/batch";var k="@waniwani/sdk",V=new Set([401,403]),C=new Set([408,425,429,500,502,503,504]);function S(e){return new T(e)}var T=class{endpointUrl;flushIntervalMs;maxBatchSize;maxBufferSize;maxRetries;retryBaseDelayMs;retryMaxDelayMs;shutdownTimeoutMs;sdkVersion;fetchFn;logger;now;sleep;apiKey;buffer=[];flushTimer;flushScheduled=!1;flushScheduledTimer;flushInFlight;inFlightCount=0;isStopped=!1;isShuttingDown=!1;constructor(t){this.endpointUrl=L(t.baseUrl,t.endpointPath??A),this.flushIntervalMs=t.flushIntervalMs??1e3,this.maxBatchSize=t.maxBatchSize??20,this.maxBufferSize=t.maxBufferSize??1e3,this.maxRetries=t.maxRetries??3,this.retryBaseDelayMs=t.retryBaseDelayMs??200,this.retryMaxDelayMs=t.retryMaxDelayMs??2e3,this.shutdownTimeoutMs=t.shutdownTimeoutMs??2e3,this.fetchFn=t.fetchFn??fetch,this.logger=t.logger??console,this.now=t.now??(()=>new Date),this.sleep=t.sleep??(n=>new Promise(r=>setTimeout(r,n))),this.apiKey=t.apiKey,this.sdkVersion=t.sdkVersion,this.flushIntervalMs>0&&(this.flushTimer=setInterval(()=>{this.flush()},this.flushIntervalMs))}enqueue(t){if(this.isStopped||this.isShuttingDown){this.logger.warn("[WaniWani] Tracking transport is stopped, dropping event %s",t.id);return}if(this.buffer.length>=this.maxBufferSize){let n=this.buffer.length-this.maxBufferSize+1;this.buffer.splice(0,n),this.logger.warn("[WaniWani] Tracking buffer overflow, dropped %d oldest event(s)",n)}if(this.buffer.push(t),this.buffer.length>=this.maxBatchSize){this.flush();return}this.scheduleMicroFlush()}pendingEvents(){return this.buffer.length+this.inFlightCount}async flush(){return this.flushInFlight?this.flushInFlight:(this.flushInFlight=this.flushLoop().finally(()=>{this.flushInFlight=void 0}),this.flushInFlight)}async shutdown(t){this.isShuttingDown=!0,this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0),this.flushScheduledTimer&&(clearTimeout(this.flushScheduledTimer),this.flushScheduledTimer=void 0,this.flushScheduled=!1);let n=t?.timeoutMs??this.shutdownTimeoutMs,r=this.flush();if(!Number.isFinite(n)||n<=0)return await r,this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()};let s=Symbol("shutdown-timeout");return await Promise.race([r.then(()=>"flushed"),this.sleep(n).then(()=>s)])===s?(this.isStopped=!0,{timedOut:!0,pendingEvents:this.pendingEvents()}):(this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()})}scheduleMicroFlush(){this.flushScheduled||(this.flushScheduled=!0,this.flushScheduledTimer=setTimeout(()=>{this.flushScheduledTimer=void 0,this.flushScheduled=!1,this.flush()},0))}async flushLoop(){for(;this.buffer.length>0&&!this.isStopped;){let t=this.buffer.splice(0,this.maxBatchSize);await this.sendBatchWithRetry(t)}}async sendBatchWithRetry(t){let n=0,r=t;for(;r.length>0&&!this.isStopped;){this.inFlightCount=r.length;let s=await this.sendBatchOnce(r);switch(this.inFlightCount=0,s.kind){case"success":return;case"auth":this.stopTransportForAuthFailure(s.status,r.length);return;case"permanent":this.logger.error("[WaniWani] Dropping %d event(s) after permanent failure: %s",r.length,s.reason);return;case"retryable":if(n>=this.maxRetries){this.logger.error("[WaniWani] Dropping %d event(s) after retry exhaustion: %s",r.length,s.reason);return}await this.sleep(this.backoffDelayMs(n)),n+=1;continue;case"partial":if(s.permanent.length>0&&this.logger.error("[WaniWani] Dropping %d event(s) rejected as permanent",s.permanent.length),s.retryable.length===0)return;if(n>=this.maxRetries){this.logger.error("[WaniWani] Dropping %d retryable event(s) after retry exhaustion",s.retryable.length);return}r=s.retryable,await this.sleep(this.backoffDelayMs(n)),n+=1;continue}}}async sendBatchOnce(t){let n;try{n=await this.fetchFn(this.endpointUrl,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,"X-WaniWani-SDK":k},body:JSON.stringify(this.makeBatchRequest(t))})}catch(i){return{kind:"retryable",reason:P(i)}}if(V.has(n.status))return{kind:"auth",status:n.status};if(C.has(n.status))return{kind:"retryable",reason:`HTTP ${n.status}`};if(!n.ok)return{kind:"permanent",reason:`HTTP ${n.status}`};let r=await U(n);if(!r?.rejected||r.rejected.length===0)return{kind:"success"};let s=this.classifyRejectedEvents(t,r.rejected);return s.retryable.length===0&&s.permanent.length===0?{kind:"success"}:{kind:"partial",retryable:s.retryable,permanent:s.permanent}}makeBatchRequest(t){return{sentAt:this.now().toISOString(),source:{sdk:k,version:this.sdkVersion??"0.0.0"},events:t}}classifyRejectedEvents(t,n){let r=new Map(t.map(o=>[o.id,o])),s=[],i=[];for(let o of n){let a=r.get(o.eventId);if(a){if(F(o)){s.push(a);continue}i.push(a)}}return{retryable:s,permanent:i}}backoffDelayMs(t){let n=this.retryBaseDelayMs*2**t;return Math.min(n,this.retryMaxDelayMs)}stopTransportForAuthFailure(t,n){this.isStopped=!0;let r=this.buffer.length;this.buffer.splice(0,r),this.logger.error("[WaniWani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)",t,n+r)}};function F(e){if(e.retryable===!0)return!0;let t=e.code.toLowerCase();return t.includes("timeout")||t.includes("temporary")||t.includes("unavailable")||t.includes("rate_limit")||t.includes("transient")||t.includes("server")}async function U(e){let t=await e.text();if(t)try{return JSON.parse(t)}catch{return}}function L(e,t){let n=e.endsWith("/")?e:`${e}/`,r=t.startsWith("/")?t.slice(1):t;return`${n}${r}`}function P(e){return e instanceof Error?e.message:String(e)}function I(e){let{baseUrl:t,apiKey:n,tracking:r}=e;function s(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}let i=n?S({baseUrl:t,apiKey:n,endpointPath:r.endpointPath,flushIntervalMs:r.flushIntervalMs,maxBatchSize:r.maxBatchSize,maxBufferSize:r.maxBufferSize,maxRetries:r.maxRetries,retryBaseDelayMs:r.retryBaseDelayMs,retryMaxDelayMs:r.retryMaxDelayMs,shutdownTimeoutMs:r.shutdownTimeoutMs}):void 0,o={async track(a){s();let h=y(a);return i?.enqueue(h),{eventId:h.id}},async flush(){s(),await i?.flush()},async shutdown(a){return s(),await i?.shutdown({timeoutMs:a?.timeoutMs??r.shutdownTimeoutMs})??{timedOut:!1,pendingEvents:0}}};return i&&W(o,r.shutdownTimeoutMs),o}function W(e,t){if(typeof process>"u"||typeof process.once!="function"||typeof process.on!="function")return;let n=()=>{e.shutdown({timeoutMs:t})};process.once("beforeExit",n),process.once("SIGINT",n),process.once("SIGTERM",n)}function O(e){let t=e?.baseUrl??"https://app.waniwani.ai",n=e?.apiKey??process.env.WANIWANI_API_KEY,r={endpointPath:e?.tracking?.endpointPath??"/api/mcp/events/v2/batch",flushIntervalMs:e?.tracking?.flushIntervalMs??1e3,maxBatchSize:e?.tracking?.maxBatchSize??20,maxBufferSize:e?.tracking?.maxBufferSize??1e3,maxRetries:e?.tracking?.maxRetries??3,retryBaseDelayMs:e?.tracking?.retryBaseDelayMs??200,retryMaxDelayMs:e?.tracking?.retryMaxDelayMs??2e3,shutdownTimeoutMs:e?.tracking?.shutdownTimeoutMs??2e3},s={baseUrl:t,apiKey:n,tracking:r};return{...I(s),_config:s}}export{g as WaniWaniError,O as waniwani};
1
+ var f=class extends Error{constructor(n,r){super(n);this.status=r;this.name="WaniWaniError"}};var _="@waniwani/sdk";function S(e){let{baseUrl:t,apiKey:n}=e;function r(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}async function s(i,a,o){let l=r(),v=`${t.replace(/\/$/,"")}${a}`,g={Authorization:`Bearer ${l}`,"X-WaniWani-SDK":_},p={method:i,headers:g};o!==void 0&&(g["Content-Type"]="application/json",p.body=JSON.stringify(o));let u=await fetch(v,p);if(!u.ok){let R=await u.text().catch(()=>"");throw new f(R||`KB API error: HTTP ${u.status}`,u.status)}return u.json()}return{async ingest(i){return s("POST","/api/mcp/kb/ingest",{files:i})},async search(i,a){return s("POST","/api/mcp/kb/search",{query:i,...a})},async sources(){return s("GET","/api/mcp/kb/sources")}}}var x="@waniwani/sdk";function T(e,t={}){let n=t.now??(()=>new Date),r=t.generateId??I,s=B(e),i=y(e.meta),a=y(e.metadata),o=C(e,i),l=c(e.eventId)??r(),v=V(e.timestamp,n),g=c(e.source)??t.source??x,p=E(e)?{...e}:void 0,u={...a};return Object.keys(i).length>0&&(u.meta=i),p&&(u.rawLegacy=p),{id:l,type:"mcp.event",name:s,source:g,timestamp:v,correlation:o,properties:D(e,s),metadata:u,rawLegacy:p}}function I(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?`evt_${crypto.randomUUID()}`:`evt_${Math.random().toString(36).slice(2,10)}_${Date.now().toString(36)}`}function D(e,t){if(!E(e))return y(e.properties);let n=A(e,t),r=y(e.properties);return{...n,...r}}function A(e,t){switch(t){case"tool.called":{let n={};return c(e.toolName)&&(n.name=e.toolName),c(e.toolType)&&(n.type=e.toolType),n}case"quote.succeeded":{let n={};return typeof e.quoteAmount=="number"&&(n.amount=e.quoteAmount),c(e.quoteCurrency)&&(n.currency=e.quoteCurrency),n}case"link.clicked":{let n={};return c(e.linkUrl)&&(n.url=e.linkUrl),n}case"purchase.completed":{let n={};return typeof e.purchaseAmount=="number"&&(n.amount=e.purchaseAmount),c(e.purchaseCurrency)&&(n.currency=e.purchaseCurrency),n}default:return{}}}function B(e){return E(e)?e.eventType:e.event}function C(e,t){let n=c(e.requestId)??m(t,["openai/requestId","requestId","mcp/requestId"]),r=c(e.sessionId)??m(t,["openai/sessionId","sessionId","conversationId","anthropic/sessionId"]),s=c(e.traceId)??m(t,["openai/traceId","traceId","mcp/traceId","openai/requestId","requestId"]),i=c(e.externalUserId)??m(t,["openai/userId","externalUserId","userId","actorId"]),a=c(e.correlationId)??m(t,["correlationId","openai/requestId"])??n,o={};return r&&(o.sessionId=r),s&&(o.traceId=s),n&&(o.requestId=n),a&&(o.correlationId=a),i&&(o.externalUserId=i),o}function V(e,t){if(e instanceof Date)return e.toISOString();if(typeof e=="string"){let n=new Date(e);if(!Number.isNaN(n.getTime()))return n.toISOString()}return t().toISOString()}function m(e,t){for(let n of t){let r=e[n];if(typeof r=="string"&&r.trim().length>0)return r}}function y(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function c(e){if(typeof e=="string"&&e.trim().length!==0)return e}function E(e){return"eventType"in e}var P="/api/mcp/events/v2/batch";var w="@waniwani/sdk",F=new Set([401,403]),U=new Set([408,425,429,500,502,503,504]);function b(e){return new k(e)}var k=class{endpointUrl;flushIntervalMs;maxBatchSize;maxBufferSize;maxRetries;retryBaseDelayMs;retryMaxDelayMs;shutdownTimeoutMs;sdkVersion;fetchFn;logger;now;sleep;apiKey;buffer=[];flushTimer;flushScheduled=!1;flushScheduledTimer;flushInFlight;inFlightCount=0;isStopped=!1;isShuttingDown=!1;constructor(t){this.endpointUrl=O(t.baseUrl,t.endpointPath??P),this.flushIntervalMs=t.flushIntervalMs??1e3,this.maxBatchSize=t.maxBatchSize??20,this.maxBufferSize=t.maxBufferSize??1e3,this.maxRetries=t.maxRetries??3,this.retryBaseDelayMs=t.retryBaseDelayMs??200,this.retryMaxDelayMs=t.retryMaxDelayMs??2e3,this.shutdownTimeoutMs=t.shutdownTimeoutMs??2e3,this.fetchFn=t.fetchFn??fetch,this.logger=t.logger??console,this.now=t.now??(()=>new Date),this.sleep=t.sleep??(n=>new Promise(r=>setTimeout(r,n))),this.apiKey=t.apiKey,this.sdkVersion=t.sdkVersion,this.flushIntervalMs>0&&(this.flushTimer=setInterval(()=>{this.flush()},this.flushIntervalMs))}enqueue(t){if(this.isStopped||this.isShuttingDown){this.logger.warn("[WaniWani] Tracking transport is stopped, dropping event %s",t.id);return}if(this.buffer.length>=this.maxBufferSize){let n=this.buffer.length-this.maxBufferSize+1;this.buffer.splice(0,n),this.logger.warn("[WaniWani] Tracking buffer overflow, dropped %d oldest event(s)",n)}if(this.buffer.push(t),this.buffer.length>=this.maxBatchSize){this.flush();return}this.scheduleMicroFlush()}pendingEvents(){return this.buffer.length+this.inFlightCount}async flush(){return this.flushInFlight?this.flushInFlight:(this.flushInFlight=this.flushLoop().finally(()=>{this.flushInFlight=void 0}),this.flushInFlight)}async shutdown(t){this.isShuttingDown=!0,this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0),this.flushScheduledTimer&&(clearTimeout(this.flushScheduledTimer),this.flushScheduledTimer=void 0,this.flushScheduled=!1);let n=t?.timeoutMs??this.shutdownTimeoutMs,r=this.flush();if(!Number.isFinite(n)||n<=0)return await r,this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()};let s=Symbol("shutdown-timeout");return await Promise.race([r.then(()=>"flushed"),this.sleep(n).then(()=>s)])===s?(this.isStopped=!0,{timedOut:!0,pendingEvents:this.pendingEvents()}):(this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()})}scheduleMicroFlush(){this.flushScheduled||(this.flushScheduled=!0,this.flushScheduledTimer=setTimeout(()=>{this.flushScheduledTimer=void 0,this.flushScheduled=!1,this.flush()},0))}async flushLoop(){for(;this.buffer.length>0&&!this.isStopped;){let t=this.buffer.splice(0,this.maxBatchSize);await this.sendBatchWithRetry(t)}}async sendBatchWithRetry(t){let n=0,r=t;for(;r.length>0&&!this.isStopped;){this.inFlightCount=r.length;let s=await this.sendBatchOnce(r);switch(this.inFlightCount=0,s.kind){case"success":return;case"auth":this.stopTransportForAuthFailure(s.status,r.length);return;case"permanent":this.logger.error("[WaniWani] Dropping %d event(s) after permanent failure: %s",r.length,s.reason);return;case"retryable":if(n>=this.maxRetries){this.logger.error("[WaniWani] Dropping %d event(s) after retry exhaustion: %s",r.length,s.reason);return}await this.sleep(this.backoffDelayMs(n)),n+=1;continue;case"partial":if(s.permanent.length>0&&this.logger.error("[WaniWani] Dropping %d event(s) rejected as permanent",s.permanent.length),s.retryable.length===0)return;if(n>=this.maxRetries){this.logger.error("[WaniWani] Dropping %d retryable event(s) after retry exhaustion",s.retryable.length);return}r=s.retryable,await this.sleep(this.backoffDelayMs(n)),n+=1;continue}}}async sendBatchOnce(t){let n;try{n=await this.fetchFn(this.endpointUrl,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,"X-WaniWani-SDK":w},body:JSON.stringify(this.makeBatchRequest(t))})}catch(i){return{kind:"retryable",reason:K(i)}}if(F.has(n.status))return{kind:"auth",status:n.status};if(U.has(n.status))return{kind:"retryable",reason:`HTTP ${n.status}`};if(!n.ok)return{kind:"permanent",reason:`HTTP ${n.status}`};let r=await L(n);if(!r?.rejected||r.rejected.length===0)return{kind:"success"};let s=this.classifyRejectedEvents(t,r.rejected);return s.retryable.length===0&&s.permanent.length===0?{kind:"success"}:{kind:"partial",retryable:s.retryable,permanent:s.permanent}}makeBatchRequest(t){return{sentAt:this.now().toISOString(),source:{sdk:w,version:this.sdkVersion??"0.0.0"},events:t}}classifyRejectedEvents(t,n){let r=new Map(t.map(a=>[a.id,a])),s=[],i=[];for(let a of n){let o=r.get(a.eventId);if(o){if(W(a)){s.push(o);continue}i.push(o)}}return{retryable:s,permanent:i}}backoffDelayMs(t){let n=this.retryBaseDelayMs*2**t;return Math.min(n,this.retryMaxDelayMs)}stopTransportForAuthFailure(t,n){this.isStopped=!0;let r=this.buffer.length;this.buffer.splice(0,r),this.logger.error("[WaniWani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)",t,n+r)}};function W(e){if(e.retryable===!0)return!0;let t=e.code.toLowerCase();return t.includes("timeout")||t.includes("temporary")||t.includes("unavailable")||t.includes("rate_limit")||t.includes("transient")||t.includes("server")}async function L(e){let t=await e.text();if(t)try{return JSON.parse(t)}catch{return}}function O(e,t){let n=e.endsWith("/")?e:`${e}/`,r=t.startsWith("/")?t.slice(1):t;return`${n}${r}`}function K(e){return e instanceof Error?e.message:String(e)}function M(e){let{baseUrl:t,apiKey:n,tracking:r}=e;function s(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}let i=n?b({baseUrl:t,apiKey:n,endpointPath:r.endpointPath,flushIntervalMs:r.flushIntervalMs,maxBatchSize:r.maxBatchSize,maxBufferSize:r.maxBufferSize,maxRetries:r.maxRetries,retryBaseDelayMs:r.retryBaseDelayMs,retryMaxDelayMs:r.retryMaxDelayMs,shutdownTimeoutMs:r.shutdownTimeoutMs}):void 0,a={async track(o){s();let l=T(o);return i?.enqueue(l),{eventId:l.id}},async flush(){s(),await i?.flush()},async shutdown(o){return s(),await i?.shutdown({timeoutMs:o?.timeoutMs??r.shutdownTimeoutMs})??{timedOut:!1,pendingEvents:0}}};return i&&j(a,r.shutdownTimeoutMs),a}function j(e,t){if(typeof process>"u"||typeof process.once!="function"||typeof process.on!="function")return;let n=()=>{e.shutdown({timeoutMs:t})};process.once("beforeExit",n),process.once("SIGINT",n),process.once("SIGTERM",n)}function q(e){let t=e?.baseUrl??"https://app.waniwani.ai",n=e?.apiKey??process.env.WANIWANI_API_KEY,r={endpointPath:e?.tracking?.endpointPath??"/api/mcp/events/v2/batch",flushIntervalMs:e?.tracking?.flushIntervalMs??1e3,maxBatchSize:e?.tracking?.maxBatchSize??20,maxBufferSize:e?.tracking?.maxBufferSize??1e3,maxRetries:e?.tracking?.maxRetries??3,retryBaseDelayMs:e?.tracking?.retryBaseDelayMs??200,retryMaxDelayMs:e?.tracking?.retryMaxDelayMs??2e3,shutdownTimeoutMs:e?.tracking?.shutdownTimeoutMs??2e3},s={baseUrl:t,apiKey:n,tracking:r},i=M(s),a=S(s);return{...i,kb:a,_config:s}}export{f as WaniWaniError,q as waniwani};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/error.ts","../src/tracking/mapper.ts","../src/tracking/transport.ts","../src/tracking/index.ts","../src/waniwani.ts"],"sourcesContent":["// WaniWani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","import type { EventType, LegacyTrackEvent, TrackInput } from \"./@types.js\";\nimport type { V2CorrelationIds, V2EventEnvelope } from \"./v2-types.js\";\n\nconst DEFAULT_SOURCE = \"@waniwani/sdk\";\n\nexport interface MapTrackEventOptions {\n\tnow?: () => Date;\n\tgenerateId?: () => string;\n\tsource?: string;\n}\n\nexport function mapTrackEventToV2(\n\tinput: TrackInput,\n\toptions: MapTrackEventOptions = {},\n): V2EventEnvelope {\n\tconst now = options.now ?? (() => new Date());\n\tconst generateId = options.generateId ?? createEventId;\n\tconst eventName = resolveEventName(input);\n\tconst meta = toRecord(input.meta);\n\tconst metadata = toRecord(input.metadata);\n\tconst correlation = resolveCorrelationIds(input, meta);\n\tconst eventId = takeNonEmptyString(input.eventId) ?? generateId();\n\tconst timestamp = normalizeTimestamp(input.timestamp, now);\n\tconst source =\n\t\ttakeNonEmptyString(input.source) ?? options.source ?? DEFAULT_SOURCE;\n\tconst rawLegacy = isLegacyTrackEvent(input) ? { ...input } : undefined;\n\n\tconst mappedMetadata: Record<string, unknown> = {\n\t\t...metadata,\n\t};\n\tif (Object.keys(meta).length > 0) mappedMetadata.meta = meta;\n\tif (rawLegacy) mappedMetadata.rawLegacy = rawLegacy;\n\n\treturn {\n\t\tid: eventId,\n\t\ttype: \"mcp.event\",\n\t\tname: eventName,\n\t\tsource,\n\t\ttimestamp,\n\t\tcorrelation,\n\t\tproperties: mapProperties(input, eventName),\n\t\tmetadata: mappedMetadata,\n\t\trawLegacy,\n\t};\n}\n\nexport function createEventId(): string {\n\tif (\n\t\ttypeof crypto !== \"undefined\" &&\n\t\ttypeof crypto.randomUUID === \"function\"\n\t) {\n\t\treturn `evt_${crypto.randomUUID()}`;\n\t}\n\n\treturn `evt_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`;\n}\n\nfunction mapProperties(\n\tinput: TrackInput,\n\teventName: EventType,\n): Record<string, unknown> {\n\tif (!isLegacyTrackEvent(input)) {\n\t\treturn toRecord(input.properties);\n\t}\n\n\tconst legacyProperties = mapLegacyProperties(input, eventName);\n\tconst explicitProperties = toRecord(input.properties);\n\treturn {\n\t\t...legacyProperties,\n\t\t...explicitProperties,\n\t};\n}\n\nfunction mapLegacyProperties(\n\tinput: LegacyTrackEvent,\n\teventName: EventType,\n): Record<string, unknown> {\n\tswitch (eventName) {\n\t\tcase \"tool.called\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (takeNonEmptyString(input.toolName)) properties.name = input.toolName;\n\t\t\tif (takeNonEmptyString(input.toolType)) properties.type = input.toolType;\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"quote.succeeded\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (typeof input.quoteAmount === \"number\") {\n\t\t\t\tproperties.amount = input.quoteAmount;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.quoteCurrency)) {\n\t\t\t\tproperties.currency = input.quoteCurrency;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"link.clicked\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (takeNonEmptyString(input.linkUrl)) properties.url = input.linkUrl;\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"purchase.completed\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (typeof input.purchaseAmount === \"number\") {\n\t\t\t\tproperties.amount = input.purchaseAmount;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.purchaseCurrency)) {\n\t\t\t\tproperties.currency = input.purchaseCurrency;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tdefault:\n\t\t\treturn {};\n\t}\n}\n\nfunction resolveEventName(input: TrackInput): EventType {\n\tif (isLegacyTrackEvent(input)) return input.eventType;\n\treturn input.event;\n}\n\nfunction resolveCorrelationIds(\n\tinput: TrackInput,\n\tmeta: Record<string, unknown>,\n): V2CorrelationIds {\n\tconst requestId =\n\t\ttakeNonEmptyString(input.requestId) ??\n\t\tpickFirstString(meta, [\"openai/requestId\", \"requestId\", \"mcp/requestId\"]);\n\n\tconst sessionId =\n\t\ttakeNonEmptyString(input.sessionId) ??\n\t\tpickFirstString(meta, [\n\t\t\t\"openai/sessionId\",\n\t\t\t\"sessionId\",\n\t\t\t\"conversationId\",\n\t\t\t\"anthropic/sessionId\",\n\t\t]);\n\n\tconst traceId =\n\t\ttakeNonEmptyString(input.traceId) ??\n\t\tpickFirstString(meta, [\n\t\t\t\"openai/traceId\",\n\t\t\t\"traceId\",\n\t\t\t\"mcp/traceId\",\n\t\t\t\"openai/requestId\",\n\t\t\t\"requestId\",\n\t\t]);\n\n\tconst externalUserId =\n\t\ttakeNonEmptyString(input.externalUserId) ??\n\t\tpickFirstString(meta, [\n\t\t\t\"openai/userId\",\n\t\t\t\"externalUserId\",\n\t\t\t\"userId\",\n\t\t\t\"actorId\",\n\t\t]);\n\n\tconst correlationId =\n\t\ttakeNonEmptyString(input.correlationId) ??\n\t\tpickFirstString(meta, [\"correlationId\", \"openai/requestId\"]) ??\n\t\trequestId;\n\n\tconst correlation: V2CorrelationIds = {};\n\tif (sessionId) correlation.sessionId = sessionId;\n\tif (traceId) correlation.traceId = traceId;\n\tif (requestId) correlation.requestId = requestId;\n\tif (correlationId) correlation.correlationId = correlationId;\n\tif (externalUserId) correlation.externalUserId = externalUserId;\n\treturn correlation;\n}\n\nfunction normalizeTimestamp(\n\tinput: string | Date | undefined,\n\tnow: () => Date,\n): string {\n\tif (input instanceof Date) return input.toISOString();\n\tif (typeof input === \"string\") {\n\t\tconst date = new Date(input);\n\t\tif (!Number.isNaN(date.getTime())) return date.toISOString();\n\t}\n\treturn now().toISOString();\n}\n\nfunction pickFirstString(\n\trecord: Record<string, unknown>,\n\tkeys: readonly string[],\n): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.trim().length > 0) {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn {};\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction takeNonEmptyString(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tif (value.trim().length === 0) return undefined;\n\treturn value;\n}\n\nfunction isLegacyTrackEvent(input: TrackInput): input is LegacyTrackEvent {\n\treturn \"eventType\" in input;\n}\n","import type {\n\tTrackingShutdownOptions,\n\tTrackingShutdownResult,\n} from \"./@types.js\";\nimport type {\n\tV2BatchRejectedEvent,\n\tV2BatchRequest,\n\tV2BatchResponse,\n\tV2EventEnvelope,\n} from \"./v2-types.js\";\n\nconst DEFAULT_ENDPOINT_PATH = \"/api/mcp/events/v2/batch\";\nconst DEFAULT_FLUSH_INTERVAL_MS = 1_000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_BUFFER_SIZE = 1_000;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY_MS = 200;\nconst DEFAULT_RETRY_MAX_DELAY_MS = 2_000;\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 2_000;\nconst SDK_NAME = \"@waniwani/sdk\";\n\nconst AUTH_FAILURE_STATUS = new Set([401, 403]);\nconst RETRYABLE_STATUS = new Set([408, 425, 429, 500, 502, 503, 504]);\n\ninterface Logger {\n\twarn: (message: string, ...args: unknown[]) => void;\n\terror: (message: string, ...args: unknown[]) => void;\n}\n\nexport interface V2TransportOptions {\n\tbaseUrl: string;\n\tapiKey: string;\n\tendpointPath?: string;\n\tflushIntervalMs?: number;\n\tmaxBatchSize?: number;\n\tmaxBufferSize?: number;\n\tmaxRetries?: number;\n\tretryBaseDelayMs?: number;\n\tretryMaxDelayMs?: number;\n\tshutdownTimeoutMs?: number;\n\tsdkVersion?: string;\n\tfetchFn?: typeof fetch;\n\tlogger?: Logger;\n\tnow?: () => Date;\n\tsleep?: (delayMs: number) => Promise<void>;\n}\n\nexport interface V2BatchTransport {\n\tenqueue: (event: V2EventEnvelope) => void;\n\tflush: () => Promise<void>;\n\tshutdown: (\n\t\toptions?: TrackingShutdownOptions,\n\t) => Promise<TrackingShutdownResult>;\n\tpendingEvents: () => number;\n}\n\ntype SendBatchResult =\n\t| { kind: \"success\" }\n\t| { kind: \"retryable\"; reason: string }\n\t| { kind: \"permanent\"; reason: string }\n\t| { kind: \"auth\"; status: number }\n\t| {\n\t\t\tkind: \"partial\";\n\t\t\tretryable: V2EventEnvelope[];\n\t\t\tpermanent: V2EventEnvelope[];\n\t };\n\nexport function createV2BatchTransport(\n\toptions: V2TransportOptions,\n): V2BatchTransport {\n\treturn new BatchingV2Transport(options);\n}\n\nclass BatchingV2Transport implements V2BatchTransport {\n\tprivate readonly endpointUrl: string;\n\tprivate readonly flushIntervalMs: number;\n\tprivate readonly maxBatchSize: number;\n\tprivate readonly maxBufferSize: number;\n\tprivate readonly maxRetries: number;\n\tprivate readonly retryBaseDelayMs: number;\n\tprivate readonly retryMaxDelayMs: number;\n\tprivate readonly shutdownTimeoutMs: number;\n\tprivate readonly sdkVersion?: string;\n\tprivate readonly fetchFn: typeof fetch;\n\tprivate readonly logger: Logger;\n\tprivate readonly now: () => Date;\n\tprivate readonly sleep: (delayMs: number) => Promise<void>;\n\tprivate readonly apiKey: string;\n\n\tprivate readonly buffer: V2EventEnvelope[] = [];\n\tprivate flushTimer: ReturnType<typeof setInterval> | undefined;\n\tprivate flushScheduled = false;\n\tprivate flushScheduledTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate flushInFlight: Promise<void> | undefined;\n\tprivate inFlightCount = 0;\n\tprivate isStopped = false;\n\tprivate isShuttingDown = false;\n\n\tconstructor(options: V2TransportOptions) {\n\t\tthis.endpointUrl = joinUrl(\n\t\t\toptions.baseUrl,\n\t\t\toptions.endpointPath ?? DEFAULT_ENDPOINT_PATH,\n\t\t);\n\t\tthis.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\t\tthis.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;\n\t\tthis.maxBufferSize = options.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;\n\t\tthis.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n\t\tthis.retryBaseDelayMs =\n\t\t\toptions.retryBaseDelayMs ?? DEFAULT_RETRY_BASE_DELAY_MS;\n\t\tthis.retryMaxDelayMs =\n\t\t\toptions.retryMaxDelayMs ?? DEFAULT_RETRY_MAX_DELAY_MS;\n\t\tthis.shutdownTimeoutMs =\n\t\t\toptions.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;\n\t\tthis.fetchFn = options.fetchFn ?? fetch;\n\t\tthis.logger = options.logger ?? console;\n\t\tthis.now = options.now ?? (() => new Date());\n\t\tthis.sleep =\n\t\t\toptions.sleep ??\n\t\t\t((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.sdkVersion = options.sdkVersion;\n\n\t\tif (this.flushIntervalMs > 0) {\n\t\t\tthis.flushTimer = setInterval(() => {\n\t\t\t\tvoid this.flush();\n\t\t\t}, this.flushIntervalMs);\n\t\t}\n\t}\n\n\tenqueue(event: V2EventEnvelope): void {\n\t\tif (this.isStopped || this.isShuttingDown) {\n\t\t\tthis.logger.warn(\n\t\t\t\t\"[WaniWani] Tracking transport is stopped, dropping event %s\",\n\t\t\t\tevent.id,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.buffer.length >= this.maxBufferSize) {\n\t\t\tconst dropCount = this.buffer.length - this.maxBufferSize + 1;\n\t\t\tthis.buffer.splice(0, dropCount);\n\t\t\tthis.logger.warn(\n\t\t\t\t\"[WaniWani] Tracking buffer overflow, dropped %d oldest event(s)\",\n\t\t\t\tdropCount,\n\t\t\t);\n\t\t}\n\n\t\tthis.buffer.push(event);\n\n\t\tif (this.buffer.length >= this.maxBatchSize) {\n\t\t\tvoid this.flush();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.scheduleMicroFlush();\n\t}\n\n\tpendingEvents(): number {\n\t\treturn this.buffer.length + this.inFlightCount;\n\t}\n\n\tasync flush(): Promise<void> {\n\t\tif (this.flushInFlight) return this.flushInFlight;\n\t\tthis.flushInFlight = this.flushLoop().finally(() => {\n\t\t\tthis.flushInFlight = undefined;\n\t\t});\n\t\treturn this.flushInFlight;\n\t}\n\n\tasync shutdown(\n\t\toptions?: TrackingShutdownOptions,\n\t): Promise<TrackingShutdownResult> {\n\t\tthis.isShuttingDown = true;\n\t\tif (this.flushTimer) {\n\t\t\tclearInterval(this.flushTimer);\n\t\t\tthis.flushTimer = undefined;\n\t\t}\n\t\tif (this.flushScheduledTimer) {\n\t\t\tclearTimeout(this.flushScheduledTimer);\n\t\t\tthis.flushScheduledTimer = undefined;\n\t\t\tthis.flushScheduled = false;\n\t\t}\n\n\t\tconst timeoutMs = options?.timeoutMs ?? this.shutdownTimeoutMs;\n\t\tconst flushPromise = this.flush();\n\n\t\tif (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n\t\t\tawait flushPromise;\n\t\t\tthis.isStopped = true;\n\t\t\treturn { timedOut: false, pendingEvents: this.pendingEvents() };\n\t\t}\n\n\t\tconst timeoutSignal = Symbol(\"shutdown-timeout\");\n\t\tconst result = await Promise.race([\n\t\t\tflushPromise.then(() => \"flushed\" as const),\n\t\t\tthis.sleep(timeoutMs).then(() => timeoutSignal),\n\t\t]);\n\n\t\tif (result === timeoutSignal) {\n\t\t\tthis.isStopped = true;\n\t\t\treturn { timedOut: true, pendingEvents: this.pendingEvents() };\n\t\t}\n\n\t\tthis.isStopped = true;\n\t\treturn { timedOut: false, pendingEvents: this.pendingEvents() };\n\t}\n\n\tprivate scheduleMicroFlush(): void {\n\t\tif (this.flushScheduled) return;\n\t\tthis.flushScheduled = true;\n\t\tthis.flushScheduledTimer = setTimeout(() => {\n\t\t\tthis.flushScheduledTimer = undefined;\n\t\t\tthis.flushScheduled = false;\n\t\t\tvoid this.flush();\n\t\t}, 0);\n\t}\n\n\tprivate async flushLoop(): Promise<void> {\n\t\twhile (this.buffer.length > 0 && !this.isStopped) {\n\t\t\tconst batch = this.buffer.splice(0, this.maxBatchSize);\n\t\t\tawait this.sendBatchWithRetry(batch);\n\t\t}\n\t}\n\n\tprivate async sendBatchWithRetry(batch: V2EventEnvelope[]): Promise<void> {\n\t\tlet attempt = 0;\n\t\tlet pendingBatch = batch;\n\n\t\twhile (pendingBatch.length > 0 && !this.isStopped) {\n\t\t\tthis.inFlightCount = pendingBatch.length;\n\t\t\tconst result = await this.sendBatchOnce(pendingBatch);\n\t\t\tthis.inFlightCount = 0;\n\n\t\t\tswitch (result.kind) {\n\t\t\t\tcase \"success\":\n\t\t\t\t\treturn;\n\t\t\t\tcase \"auth\":\n\t\t\t\t\tthis.stopTransportForAuthFailure(result.status, pendingBatch.length);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"permanent\":\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) after permanent failure: %s\",\n\t\t\t\t\t\tpendingBatch.length,\n\t\t\t\t\t\tresult.reason,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"retryable\":\n\t\t\t\t\tif (attempt >= this.maxRetries) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) after retry exhaustion: %s\",\n\t\t\t\t\t\t\tpendingBatch.length,\n\t\t\t\t\t\t\tresult.reason,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait this.sleep(this.backoffDelayMs(attempt));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase \"partial\":\n\t\t\t\t\tif (result.permanent.length > 0) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) rejected as permanent\",\n\t\t\t\t\t\t\tresult.permanent.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (result.retryable.length === 0) return;\n\t\t\t\t\tif (attempt >= this.maxRetries) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d retryable event(s) after retry exhaustion\",\n\t\t\t\t\t\t\tresult.retryable.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tpendingBatch = result.retryable;\n\t\t\t\t\tawait this.sleep(this.backoffDelayMs(attempt));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async sendBatchOnce(\n\t\tevents: V2EventEnvelope[],\n\t): Promise<SendBatchResult> {\n\t\tlet response: Response;\n\n\t\ttry {\n\t\t\tresponse = await this.fetchFn(this.endpointUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(this.makeBatchRequest(events)),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tkind: \"retryable\",\n\t\t\t\treason: getErrorMessage(error),\n\t\t\t};\n\t\t}\n\n\t\tif (AUTH_FAILURE_STATUS.has(response.status)) {\n\t\t\treturn { kind: \"auth\", status: response.status };\n\t\t}\n\n\t\tif (RETRYABLE_STATUS.has(response.status)) {\n\t\t\treturn {\n\t\t\t\tkind: \"retryable\",\n\t\t\t\treason: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\treturn {\n\t\t\t\tkind: \"permanent\",\n\t\t\t\treason: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\n\t\tconst data = await parseJsonResponse<V2BatchResponse>(response);\n\t\tif (!data?.rejected || data.rejected.length === 0) {\n\t\t\treturn { kind: \"success\" };\n\t\t}\n\n\t\tconst partial = this.classifyRejectedEvents(events, data.rejected);\n\t\tif (partial.retryable.length === 0 && partial.permanent.length === 0) {\n\t\t\treturn { kind: \"success\" };\n\t\t}\n\n\t\treturn {\n\t\t\tkind: \"partial\",\n\t\t\tretryable: partial.retryable,\n\t\t\tpermanent: partial.permanent,\n\t\t};\n\t}\n\n\tprivate makeBatchRequest(events: V2EventEnvelope[]): V2BatchRequest {\n\t\treturn {\n\t\t\tsentAt: this.now().toISOString(),\n\t\t\tsource: {\n\t\t\t\tsdk: SDK_NAME,\n\t\t\t\tversion: this.sdkVersion ?? \"0.0.0\",\n\t\t\t},\n\t\t\tevents,\n\t\t};\n\t}\n\n\tprivate classifyRejectedEvents(\n\t\tevents: V2EventEnvelope[],\n\t\trejected: V2BatchRejectedEvent[],\n\t): {\n\t\tretryable: V2EventEnvelope[];\n\t\tpermanent: V2EventEnvelope[];\n\t} {\n\t\tconst byId = new Map(events.map((event) => [event.id, event]));\n\t\tconst retryable: V2EventEnvelope[] = [];\n\t\tconst permanent: V2EventEnvelope[] = [];\n\n\t\tfor (const rejectedEvent of rejected) {\n\t\t\tconst event = byId.get(rejectedEvent.eventId);\n\t\t\tif (!event) continue;\n\t\t\tif (isRetryableRejectedEvent(rejectedEvent)) {\n\t\t\t\tretryable.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpermanent.push(event);\n\t\t}\n\n\t\treturn { retryable, permanent };\n\t}\n\n\tprivate backoffDelayMs(attempt: number): number {\n\t\tconst rawDelay = this.retryBaseDelayMs * 2 ** attempt;\n\t\treturn Math.min(rawDelay, this.retryMaxDelayMs);\n\t}\n\n\tprivate stopTransportForAuthFailure(\n\t\tstatus: number,\n\t\trejectedCount: number,\n\t): void {\n\t\tthis.isStopped = true;\n\t\tconst buffered = this.buffer.length;\n\t\tthis.buffer.splice(0, buffered);\n\t\tthis.logger.error(\n\t\t\t\"[WaniWani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)\",\n\t\t\tstatus,\n\t\t\trejectedCount + buffered,\n\t\t);\n\t}\n}\n\nfunction isRetryableRejectedEvent(\n\trejectedEvent: V2BatchRejectedEvent,\n): boolean {\n\tif (rejectedEvent.retryable === true) return true;\n\tconst code = rejectedEvent.code.toLowerCase();\n\treturn (\n\t\tcode.includes(\"timeout\") ||\n\t\tcode.includes(\"temporary\") ||\n\t\tcode.includes(\"unavailable\") ||\n\t\tcode.includes(\"rate_limit\") ||\n\t\tcode.includes(\"transient\") ||\n\t\tcode.includes(\"server\")\n\t);\n}\n\nasync function parseJsonResponse<T>(\n\tresponse: Response,\n): Promise<T | undefined> {\n\tconst body = await response.text();\n\tif (!body) return undefined;\n\ttry {\n\t\treturn JSON.parse(body) as T;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction joinUrl(baseUrl: string, endpointPath: string): string {\n\tconst normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n\tconst normalizedPath = endpointPath.startsWith(\"/\")\n\t\t? endpointPath.slice(1)\n\t\t: endpointPath;\n\treturn `${normalizedBase}${normalizedPath}`;\n}\n\nfunction getErrorMessage(error: unknown): string {\n\tif (error instanceof Error) return error.message;\n\treturn String(error);\n}\n","// Tracking Module\n\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tTrackInput,\n\tTrackingClient,\n\tTrackingShutdownOptions,\n} from \"./@types.js\";\nimport { mapTrackEventToV2 } from \"./mapper.js\";\nimport { createV2BatchTransport } from \"./transport.js\";\n\n// Re-export types\nexport type {\n\tEventType,\n\tLegacyTrackEvent,\n\tLinkClickedProperties,\n\tPurchaseCompletedProperties,\n\tQuoteSucceededProperties,\n\tToolCalledProperties,\n\tTrackEvent,\n\tTrackInput,\n\tTrackingClient,\n\tTrackingConfig,\n\tTrackingShutdownOptions,\n\tTrackingShutdownResult,\n} from \"./@types.js\";\nexport { createEventId, mapTrackEventToV2 } from \"./mapper.js\";\nexport type {\n\tV2BatchRejectedEvent,\n\tV2BatchRequest,\n\tV2BatchResponse,\n\tV2CorrelationIds,\n\tV2EnvelopeType,\n\tV2EventEnvelope,\n} from \"./v2-types.js\";\n\nexport function createTrackingClient(config: InternalConfig): TrackingClient {\n\tconst { baseUrl, apiKey, tracking } = config;\n\n\tfunction requireApiKey(): string {\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tconst transport = apiKey\n\t\t? createV2BatchTransport({\n\t\t\t\tbaseUrl,\n\t\t\t\tapiKey,\n\t\t\t\tendpointPath: tracking.endpointPath,\n\t\t\t\tflushIntervalMs: tracking.flushIntervalMs,\n\t\t\t\tmaxBatchSize: tracking.maxBatchSize,\n\t\t\t\tmaxBufferSize: tracking.maxBufferSize,\n\t\t\t\tmaxRetries: tracking.maxRetries,\n\t\t\t\tretryBaseDelayMs: tracking.retryBaseDelayMs,\n\t\t\t\tretryMaxDelayMs: tracking.retryMaxDelayMs,\n\t\t\t\tshutdownTimeoutMs: tracking.shutdownTimeoutMs,\n\t\t\t})\n\t\t: undefined;\n\n\tconst client: TrackingClient = {\n\t\tasync track(event: TrackInput): Promise<{ eventId: string }> {\n\t\t\trequireApiKey();\n\t\t\tconst mappedEvent = mapTrackEventToV2(event);\n\t\t\ttransport?.enqueue(mappedEvent);\n\t\t\treturn { eventId: mappedEvent.id };\n\t\t},\n\t\tasync flush(): Promise<void> {\n\t\t\trequireApiKey();\n\t\t\tawait transport?.flush();\n\t\t},\n\t\tasync shutdown(options?: TrackingShutdownOptions) {\n\t\t\trequireApiKey();\n\t\t\treturn (\n\t\t\t\t(await transport?.shutdown({\n\t\t\t\t\ttimeoutMs: options?.timeoutMs ?? tracking.shutdownTimeoutMs,\n\t\t\t\t})) ?? { timedOut: false, pendingEvents: 0 }\n\t\t\t);\n\t\t},\n\t};\n\n\tif (transport) {\n\t\tattachShutdownHooks(client, tracking.shutdownTimeoutMs);\n\t}\n\treturn client;\n}\n\nfunction attachShutdownHooks(\n\tclient: TrackingClient,\n\tdefaultTimeoutMs: number,\n): void {\n\tif (\n\t\ttypeof process === \"undefined\" ||\n\t\ttypeof process.once !== \"function\" ||\n\t\ttypeof process.on !== \"function\"\n\t) {\n\t\treturn;\n\t}\n\n\tconst shutdown = () => {\n\t\tvoid client.shutdown({ timeoutMs: defaultTimeoutMs });\n\t};\n\n\tprocess.once(\"beforeExit\", shutdown);\n\tprocess.once(\"SIGINT\", shutdown);\n\tprocess.once(\"SIGTERM\", shutdown);\n}\n","// WaniWani SDK - Main Entry\n\nimport { createTrackingClient } from \"./tracking/index.js\";\nimport type { WaniWaniClient, WaniWaniConfig } from \"./types.js\";\n\n/**\n * Create a WaniWani SDK client\n *\n * @param config - Configuration options\n * @returns A fully typed WaniWani client\n *\n * @example\n * ```typescript\n * import { waniwani } from \"@waniwani/sdk\";\n * import { toNextJsHandler } from \"@waniwani/sdk/next-js\";\n *\n * const wani = waniwani({ apiKey: \"...\" });\n *\n * // Next.js route handler\n * export const { GET, POST } = toNextJsHandler(wani, {\n * chat: { systemPrompt: \"You are a helpful assistant.\" },\n * });\n * ```\n */\nexport function waniwani(config?: WaniWaniConfig): WaniWaniClient {\n\tconst baseUrl = config?.baseUrl ?? \"https://app.waniwani.ai\";\n\tconst apiKey = config?.apiKey ?? process.env.WANIWANI_API_KEY;\n\tconst trackingConfig = {\n\t\tendpointPath: config?.tracking?.endpointPath ?? \"/api/mcp/events/v2/batch\",\n\t\tflushIntervalMs: config?.tracking?.flushIntervalMs ?? 1_000,\n\t\tmaxBatchSize: config?.tracking?.maxBatchSize ?? 20,\n\t\tmaxBufferSize: config?.tracking?.maxBufferSize ?? 1_000,\n\t\tmaxRetries: config?.tracking?.maxRetries ?? 3,\n\t\tretryBaseDelayMs: config?.tracking?.retryBaseDelayMs ?? 200,\n\t\tretryMaxDelayMs: config?.tracking?.retryMaxDelayMs ?? 2_000,\n\t\tshutdownTimeoutMs: config?.tracking?.shutdownTimeoutMs ?? 2_000,\n\t};\n\n\tconst internalConfig = { baseUrl, apiKey, tracking: trackingConfig };\n\n\t// Compose client from modules\n\tconst trackingClient = createTrackingClient(internalConfig);\n\n\treturn {\n\t\t...trackingClient,\n\t\t_config: internalConfig,\n\t};\n}\n"],"mappings":"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","names":["WaniWaniError","message","status","DEFAULT_SOURCE","mapTrackEventToV2","input","options","now","generateId","createEventId","eventName","resolveEventName","meta","toRecord","metadata","correlation","resolveCorrelationIds","eventId","takeNonEmptyString","timestamp","normalizeTimestamp","source","rawLegacy","isLegacyTrackEvent","mappedMetadata","mapProperties","legacyProperties","mapLegacyProperties","explicitProperties","properties","requestId","pickFirstString","sessionId","traceId","externalUserId","correlationId","date","record","keys","key","value","DEFAULT_ENDPOINT_PATH","SDK_NAME","AUTH_FAILURE_STATUS","RETRYABLE_STATUS","createV2BatchTransport","options","BatchingV2Transport","joinUrl","DEFAULT_ENDPOINT_PATH","delayMs","resolve","event","dropCount","timeoutMs","flushPromise","timeoutSignal","batch","attempt","pendingBatch","result","events","response","error","getErrorMessage","data","parseJsonResponse","partial","rejected","byId","retryable","permanent","rejectedEvent","isRetryableRejectedEvent","rawDelay","status","rejectedCount","buffered","code","body","baseUrl","endpointPath","normalizedBase","normalizedPath","createTrackingClient","config","baseUrl","apiKey","tracking","requireApiKey","transport","createV2BatchTransport","client","event","mappedEvent","mapTrackEventToV2","options","attachShutdownHooks","defaultTimeoutMs","shutdown","waniwani","config","baseUrl","apiKey","trackingConfig","internalConfig","createTrackingClient"]}
1
+ {"version":3,"sources":["../src/error.ts","../src/kb/client.ts","../src/tracking/mapper.ts","../src/tracking/transport.ts","../src/tracking/index.ts","../src/waniwani.ts"],"sourcesContent":["// WaniWani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","// KB Client — thin HTTP wrapper for knowledge base API\n\nimport { WaniWaniError } from \"../error.js\";\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tKbClient,\n\tKbIngestFile,\n\tKbIngestResult,\n\tKbSearchOptions,\n\tKbSource,\n\tSearchResult,\n} from \"./types.js\";\n\nconst SDK_NAME = \"@waniwani/sdk\";\n\nexport function createKbClient(config: InternalConfig): KbClient {\n\tconst { baseUrl, apiKey } = config;\n\n\tfunction requireApiKey(): string {\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tasync function request<T>(\n\t\tmethod: \"GET\" | \"POST\",\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst key = requireApiKey();\n\t\tconst url = `${baseUrl.replace(/\\/$/, \"\")}${path}`;\n\n\t\tconst headers: Record<string, string> = {\n\t\t\tAuthorization: `Bearer ${key}`,\n\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t};\n\n\t\tconst init: RequestInit = { method, headers };\n\n\t\tif (body !== undefined) {\n\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\t\t\tinit.body = JSON.stringify(body);\n\t\t}\n\n\t\tconst response = await fetch(url, init);\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\tthrow new WaniWaniError(\n\t\t\t\ttext || `KB API error: HTTP ${response.status}`,\n\t\t\t\tresponse.status,\n\t\t\t);\n\t\t}\n\n\t\treturn response.json() as Promise<T>;\n\t}\n\n\treturn {\n\t\tasync ingest(files: KbIngestFile[]): Promise<KbIngestResult> {\n\t\t\treturn request<KbIngestResult>(\"POST\", \"/api/mcp/kb/ingest\", {\n\t\t\t\tfiles,\n\t\t\t});\n\t\t},\n\n\t\tasync search(\n\t\t\tquery: string,\n\t\t\toptions?: KbSearchOptions,\n\t\t): Promise<SearchResult[]> {\n\t\t\treturn request<SearchResult[]>(\"POST\", \"/api/mcp/kb/search\", {\n\t\t\t\tquery,\n\t\t\t\t...options,\n\t\t\t});\n\t\t},\n\n\t\tasync sources(): Promise<KbSource[]> {\n\t\t\treturn request<KbSource[]>(\"GET\", \"/api/mcp/kb/sources\");\n\t\t},\n\t};\n}\n","import type { EventType, LegacyTrackEvent, TrackInput } from \"./@types.js\";\nimport type { V2CorrelationIds, V2EventEnvelope } from \"./v2-types.js\";\n\nconst DEFAULT_SOURCE = \"@waniwani/sdk\";\n\nexport interface MapTrackEventOptions {\n\tnow?: () => Date;\n\tgenerateId?: () => string;\n\tsource?: string;\n}\n\nexport function mapTrackEventToV2(\n\tinput: TrackInput,\n\toptions: MapTrackEventOptions = {},\n): V2EventEnvelope {\n\tconst now = options.now ?? (() => new Date());\n\tconst generateId = options.generateId ?? createEventId;\n\tconst eventName = resolveEventName(input);\n\tconst meta = toRecord(input.meta);\n\tconst metadata = toRecord(input.metadata);\n\tconst correlation = resolveCorrelationIds(input, meta);\n\tconst eventId = takeNonEmptyString(input.eventId) ?? generateId();\n\tconst timestamp = normalizeTimestamp(input.timestamp, now);\n\tconst source =\n\t\ttakeNonEmptyString(input.source) ?? options.source ?? DEFAULT_SOURCE;\n\tconst rawLegacy = isLegacyTrackEvent(input) ? { ...input } : undefined;\n\n\tconst mappedMetadata: Record<string, unknown> = {\n\t\t...metadata,\n\t};\n\tif (Object.keys(meta).length > 0) mappedMetadata.meta = meta;\n\tif (rawLegacy) mappedMetadata.rawLegacy = rawLegacy;\n\n\treturn {\n\t\tid: eventId,\n\t\ttype: \"mcp.event\",\n\t\tname: eventName,\n\t\tsource,\n\t\ttimestamp,\n\t\tcorrelation,\n\t\tproperties: mapProperties(input, eventName),\n\t\tmetadata: mappedMetadata,\n\t\trawLegacy,\n\t};\n}\n\nexport function createEventId(): string {\n\tif (\n\t\ttypeof crypto !== \"undefined\" &&\n\t\ttypeof crypto.randomUUID === \"function\"\n\t) {\n\t\treturn `evt_${crypto.randomUUID()}`;\n\t}\n\n\treturn `evt_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`;\n}\n\nfunction mapProperties(\n\tinput: TrackInput,\n\teventName: EventType,\n): Record<string, unknown> {\n\tif (!isLegacyTrackEvent(input)) {\n\t\treturn toRecord(input.properties);\n\t}\n\n\tconst legacyProperties = mapLegacyProperties(input, eventName);\n\tconst explicitProperties = toRecord(input.properties);\n\treturn {\n\t\t...legacyProperties,\n\t\t...explicitProperties,\n\t};\n}\n\nfunction mapLegacyProperties(\n\tinput: LegacyTrackEvent,\n\teventName: EventType,\n): Record<string, unknown> {\n\tswitch (eventName) {\n\t\tcase \"tool.called\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (takeNonEmptyString(input.toolName)) properties.name = input.toolName;\n\t\t\tif (takeNonEmptyString(input.toolType)) properties.type = input.toolType;\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"quote.succeeded\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (typeof input.quoteAmount === \"number\") {\n\t\t\t\tproperties.amount = input.quoteAmount;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.quoteCurrency)) {\n\t\t\t\tproperties.currency = input.quoteCurrency;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"link.clicked\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (takeNonEmptyString(input.linkUrl)) properties.url = input.linkUrl;\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"purchase.completed\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (typeof input.purchaseAmount === \"number\") {\n\t\t\t\tproperties.amount = input.purchaseAmount;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.purchaseCurrency)) {\n\t\t\t\tproperties.currency = input.purchaseCurrency;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tdefault:\n\t\t\treturn {};\n\t}\n}\n\nfunction resolveEventName(input: TrackInput): EventType {\n\tif (isLegacyTrackEvent(input)) return input.eventType;\n\treturn input.event;\n}\n\nfunction resolveCorrelationIds(\n\tinput: TrackInput,\n\tmeta: Record<string, unknown>,\n): V2CorrelationIds {\n\tconst requestId =\n\t\ttakeNonEmptyString(input.requestId) ??\n\t\tpickFirstString(meta, [\"openai/requestId\", \"requestId\", \"mcp/requestId\"]);\n\n\tconst sessionId =\n\t\ttakeNonEmptyString(input.sessionId) ??\n\t\tpickFirstString(meta, [\n\t\t\t\"openai/sessionId\",\n\t\t\t\"sessionId\",\n\t\t\t\"conversationId\",\n\t\t\t\"anthropic/sessionId\",\n\t\t]);\n\n\tconst traceId =\n\t\ttakeNonEmptyString(input.traceId) ??\n\t\tpickFirstString(meta, [\n\t\t\t\"openai/traceId\",\n\t\t\t\"traceId\",\n\t\t\t\"mcp/traceId\",\n\t\t\t\"openai/requestId\",\n\t\t\t\"requestId\",\n\t\t]);\n\n\tconst externalUserId =\n\t\ttakeNonEmptyString(input.externalUserId) ??\n\t\tpickFirstString(meta, [\n\t\t\t\"openai/userId\",\n\t\t\t\"externalUserId\",\n\t\t\t\"userId\",\n\t\t\t\"actorId\",\n\t\t]);\n\n\tconst correlationId =\n\t\ttakeNonEmptyString(input.correlationId) ??\n\t\tpickFirstString(meta, [\"correlationId\", \"openai/requestId\"]) ??\n\t\trequestId;\n\n\tconst correlation: V2CorrelationIds = {};\n\tif (sessionId) correlation.sessionId = sessionId;\n\tif (traceId) correlation.traceId = traceId;\n\tif (requestId) correlation.requestId = requestId;\n\tif (correlationId) correlation.correlationId = correlationId;\n\tif (externalUserId) correlation.externalUserId = externalUserId;\n\treturn correlation;\n}\n\nfunction normalizeTimestamp(\n\tinput: string | Date | undefined,\n\tnow: () => Date,\n): string {\n\tif (input instanceof Date) return input.toISOString();\n\tif (typeof input === \"string\") {\n\t\tconst date = new Date(input);\n\t\tif (!Number.isNaN(date.getTime())) return date.toISOString();\n\t}\n\treturn now().toISOString();\n}\n\nfunction pickFirstString(\n\trecord: Record<string, unknown>,\n\tkeys: readonly string[],\n): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.trim().length > 0) {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn {};\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction takeNonEmptyString(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tif (value.trim().length === 0) return undefined;\n\treturn value;\n}\n\nfunction isLegacyTrackEvent(input: TrackInput): input is LegacyTrackEvent {\n\treturn \"eventType\" in input;\n}\n","import type {\n\tTrackingShutdownOptions,\n\tTrackingShutdownResult,\n} from \"./@types.js\";\nimport type {\n\tV2BatchRejectedEvent,\n\tV2BatchRequest,\n\tV2BatchResponse,\n\tV2EventEnvelope,\n} from \"./v2-types.js\";\n\nconst DEFAULT_ENDPOINT_PATH = \"/api/mcp/events/v2/batch\";\nconst DEFAULT_FLUSH_INTERVAL_MS = 1_000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_BUFFER_SIZE = 1_000;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY_MS = 200;\nconst DEFAULT_RETRY_MAX_DELAY_MS = 2_000;\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 2_000;\nconst SDK_NAME = \"@waniwani/sdk\";\n\nconst AUTH_FAILURE_STATUS = new Set([401, 403]);\nconst RETRYABLE_STATUS = new Set([408, 425, 429, 500, 502, 503, 504]);\n\ninterface Logger {\n\twarn: (message: string, ...args: unknown[]) => void;\n\terror: (message: string, ...args: unknown[]) => void;\n}\n\nexport interface V2TransportOptions {\n\tbaseUrl: string;\n\tapiKey: string;\n\tendpointPath?: string;\n\tflushIntervalMs?: number;\n\tmaxBatchSize?: number;\n\tmaxBufferSize?: number;\n\tmaxRetries?: number;\n\tretryBaseDelayMs?: number;\n\tretryMaxDelayMs?: number;\n\tshutdownTimeoutMs?: number;\n\tsdkVersion?: string;\n\tfetchFn?: typeof fetch;\n\tlogger?: Logger;\n\tnow?: () => Date;\n\tsleep?: (delayMs: number) => Promise<void>;\n}\n\nexport interface V2BatchTransport {\n\tenqueue: (event: V2EventEnvelope) => void;\n\tflush: () => Promise<void>;\n\tshutdown: (\n\t\toptions?: TrackingShutdownOptions,\n\t) => Promise<TrackingShutdownResult>;\n\tpendingEvents: () => number;\n}\n\ntype SendBatchResult =\n\t| { kind: \"success\" }\n\t| { kind: \"retryable\"; reason: string }\n\t| { kind: \"permanent\"; reason: string }\n\t| { kind: \"auth\"; status: number }\n\t| {\n\t\t\tkind: \"partial\";\n\t\t\tretryable: V2EventEnvelope[];\n\t\t\tpermanent: V2EventEnvelope[];\n\t };\n\nexport function createV2BatchTransport(\n\toptions: V2TransportOptions,\n): V2BatchTransport {\n\treturn new BatchingV2Transport(options);\n}\n\nclass BatchingV2Transport implements V2BatchTransport {\n\tprivate readonly endpointUrl: string;\n\tprivate readonly flushIntervalMs: number;\n\tprivate readonly maxBatchSize: number;\n\tprivate readonly maxBufferSize: number;\n\tprivate readonly maxRetries: number;\n\tprivate readonly retryBaseDelayMs: number;\n\tprivate readonly retryMaxDelayMs: number;\n\tprivate readonly shutdownTimeoutMs: number;\n\tprivate readonly sdkVersion?: string;\n\tprivate readonly fetchFn: typeof fetch;\n\tprivate readonly logger: Logger;\n\tprivate readonly now: () => Date;\n\tprivate readonly sleep: (delayMs: number) => Promise<void>;\n\tprivate readonly apiKey: string;\n\n\tprivate readonly buffer: V2EventEnvelope[] = [];\n\tprivate flushTimer: ReturnType<typeof setInterval> | undefined;\n\tprivate flushScheduled = false;\n\tprivate flushScheduledTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate flushInFlight: Promise<void> | undefined;\n\tprivate inFlightCount = 0;\n\tprivate isStopped = false;\n\tprivate isShuttingDown = false;\n\n\tconstructor(options: V2TransportOptions) {\n\t\tthis.endpointUrl = joinUrl(\n\t\t\toptions.baseUrl,\n\t\t\toptions.endpointPath ?? DEFAULT_ENDPOINT_PATH,\n\t\t);\n\t\tthis.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\t\tthis.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;\n\t\tthis.maxBufferSize = options.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;\n\t\tthis.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n\t\tthis.retryBaseDelayMs =\n\t\t\toptions.retryBaseDelayMs ?? DEFAULT_RETRY_BASE_DELAY_MS;\n\t\tthis.retryMaxDelayMs =\n\t\t\toptions.retryMaxDelayMs ?? DEFAULT_RETRY_MAX_DELAY_MS;\n\t\tthis.shutdownTimeoutMs =\n\t\t\toptions.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;\n\t\tthis.fetchFn = options.fetchFn ?? fetch;\n\t\tthis.logger = options.logger ?? console;\n\t\tthis.now = options.now ?? (() => new Date());\n\t\tthis.sleep =\n\t\t\toptions.sleep ??\n\t\t\t((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.sdkVersion = options.sdkVersion;\n\n\t\tif (this.flushIntervalMs > 0) {\n\t\t\tthis.flushTimer = setInterval(() => {\n\t\t\t\tvoid this.flush();\n\t\t\t}, this.flushIntervalMs);\n\t\t}\n\t}\n\n\tenqueue(event: V2EventEnvelope): void {\n\t\tif (this.isStopped || this.isShuttingDown) {\n\t\t\tthis.logger.warn(\n\t\t\t\t\"[WaniWani] Tracking transport is stopped, dropping event %s\",\n\t\t\t\tevent.id,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.buffer.length >= this.maxBufferSize) {\n\t\t\tconst dropCount = this.buffer.length - this.maxBufferSize + 1;\n\t\t\tthis.buffer.splice(0, dropCount);\n\t\t\tthis.logger.warn(\n\t\t\t\t\"[WaniWani] Tracking buffer overflow, dropped %d oldest event(s)\",\n\t\t\t\tdropCount,\n\t\t\t);\n\t\t}\n\n\t\tthis.buffer.push(event);\n\n\t\tif (this.buffer.length >= this.maxBatchSize) {\n\t\t\tvoid this.flush();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.scheduleMicroFlush();\n\t}\n\n\tpendingEvents(): number {\n\t\treturn this.buffer.length + this.inFlightCount;\n\t}\n\n\tasync flush(): Promise<void> {\n\t\tif (this.flushInFlight) return this.flushInFlight;\n\t\tthis.flushInFlight = this.flushLoop().finally(() => {\n\t\t\tthis.flushInFlight = undefined;\n\t\t});\n\t\treturn this.flushInFlight;\n\t}\n\n\tasync shutdown(\n\t\toptions?: TrackingShutdownOptions,\n\t): Promise<TrackingShutdownResult> {\n\t\tthis.isShuttingDown = true;\n\t\tif (this.flushTimer) {\n\t\t\tclearInterval(this.flushTimer);\n\t\t\tthis.flushTimer = undefined;\n\t\t}\n\t\tif (this.flushScheduledTimer) {\n\t\t\tclearTimeout(this.flushScheduledTimer);\n\t\t\tthis.flushScheduledTimer = undefined;\n\t\t\tthis.flushScheduled = false;\n\t\t}\n\n\t\tconst timeoutMs = options?.timeoutMs ?? this.shutdownTimeoutMs;\n\t\tconst flushPromise = this.flush();\n\n\t\tif (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n\t\t\tawait flushPromise;\n\t\t\tthis.isStopped = true;\n\t\t\treturn { timedOut: false, pendingEvents: this.pendingEvents() };\n\t\t}\n\n\t\tconst timeoutSignal = Symbol(\"shutdown-timeout\");\n\t\tconst result = await Promise.race([\n\t\t\tflushPromise.then(() => \"flushed\" as const),\n\t\t\tthis.sleep(timeoutMs).then(() => timeoutSignal),\n\t\t]);\n\n\t\tif (result === timeoutSignal) {\n\t\t\tthis.isStopped = true;\n\t\t\treturn { timedOut: true, pendingEvents: this.pendingEvents() };\n\t\t}\n\n\t\tthis.isStopped = true;\n\t\treturn { timedOut: false, pendingEvents: this.pendingEvents() };\n\t}\n\n\tprivate scheduleMicroFlush(): void {\n\t\tif (this.flushScheduled) return;\n\t\tthis.flushScheduled = true;\n\t\tthis.flushScheduledTimer = setTimeout(() => {\n\t\t\tthis.flushScheduledTimer = undefined;\n\t\t\tthis.flushScheduled = false;\n\t\t\tvoid this.flush();\n\t\t}, 0);\n\t}\n\n\tprivate async flushLoop(): Promise<void> {\n\t\twhile (this.buffer.length > 0 && !this.isStopped) {\n\t\t\tconst batch = this.buffer.splice(0, this.maxBatchSize);\n\t\t\tawait this.sendBatchWithRetry(batch);\n\t\t}\n\t}\n\n\tprivate async sendBatchWithRetry(batch: V2EventEnvelope[]): Promise<void> {\n\t\tlet attempt = 0;\n\t\tlet pendingBatch = batch;\n\n\t\twhile (pendingBatch.length > 0 && !this.isStopped) {\n\t\t\tthis.inFlightCount = pendingBatch.length;\n\t\t\tconst result = await this.sendBatchOnce(pendingBatch);\n\t\t\tthis.inFlightCount = 0;\n\n\t\t\tswitch (result.kind) {\n\t\t\t\tcase \"success\":\n\t\t\t\t\treturn;\n\t\t\t\tcase \"auth\":\n\t\t\t\t\tthis.stopTransportForAuthFailure(result.status, pendingBatch.length);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"permanent\":\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) after permanent failure: %s\",\n\t\t\t\t\t\tpendingBatch.length,\n\t\t\t\t\t\tresult.reason,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"retryable\":\n\t\t\t\t\tif (attempt >= this.maxRetries) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) after retry exhaustion: %s\",\n\t\t\t\t\t\t\tpendingBatch.length,\n\t\t\t\t\t\t\tresult.reason,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait this.sleep(this.backoffDelayMs(attempt));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase \"partial\":\n\t\t\t\t\tif (result.permanent.length > 0) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) rejected as permanent\",\n\t\t\t\t\t\t\tresult.permanent.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (result.retryable.length === 0) return;\n\t\t\t\t\tif (attempt >= this.maxRetries) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d retryable event(s) after retry exhaustion\",\n\t\t\t\t\t\t\tresult.retryable.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tpendingBatch = result.retryable;\n\t\t\t\t\tawait this.sleep(this.backoffDelayMs(attempt));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async sendBatchOnce(\n\t\tevents: V2EventEnvelope[],\n\t): Promise<SendBatchResult> {\n\t\tlet response: Response;\n\n\t\ttry {\n\t\t\tresponse = await this.fetchFn(this.endpointUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(this.makeBatchRequest(events)),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tkind: \"retryable\",\n\t\t\t\treason: getErrorMessage(error),\n\t\t\t};\n\t\t}\n\n\t\tif (AUTH_FAILURE_STATUS.has(response.status)) {\n\t\t\treturn { kind: \"auth\", status: response.status };\n\t\t}\n\n\t\tif (RETRYABLE_STATUS.has(response.status)) {\n\t\t\treturn {\n\t\t\t\tkind: \"retryable\",\n\t\t\t\treason: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\treturn {\n\t\t\t\tkind: \"permanent\",\n\t\t\t\treason: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\n\t\tconst data = await parseJsonResponse<V2BatchResponse>(response);\n\t\tif (!data?.rejected || data.rejected.length === 0) {\n\t\t\treturn { kind: \"success\" };\n\t\t}\n\n\t\tconst partial = this.classifyRejectedEvents(events, data.rejected);\n\t\tif (partial.retryable.length === 0 && partial.permanent.length === 0) {\n\t\t\treturn { kind: \"success\" };\n\t\t}\n\n\t\treturn {\n\t\t\tkind: \"partial\",\n\t\t\tretryable: partial.retryable,\n\t\t\tpermanent: partial.permanent,\n\t\t};\n\t}\n\n\tprivate makeBatchRequest(events: V2EventEnvelope[]): V2BatchRequest {\n\t\treturn {\n\t\t\tsentAt: this.now().toISOString(),\n\t\t\tsource: {\n\t\t\t\tsdk: SDK_NAME,\n\t\t\t\tversion: this.sdkVersion ?? \"0.0.0\",\n\t\t\t},\n\t\t\tevents,\n\t\t};\n\t}\n\n\tprivate classifyRejectedEvents(\n\t\tevents: V2EventEnvelope[],\n\t\trejected: V2BatchRejectedEvent[],\n\t): {\n\t\tretryable: V2EventEnvelope[];\n\t\tpermanent: V2EventEnvelope[];\n\t} {\n\t\tconst byId = new Map(events.map((event) => [event.id, event]));\n\t\tconst retryable: V2EventEnvelope[] = [];\n\t\tconst permanent: V2EventEnvelope[] = [];\n\n\t\tfor (const rejectedEvent of rejected) {\n\t\t\tconst event = byId.get(rejectedEvent.eventId);\n\t\t\tif (!event) continue;\n\t\t\tif (isRetryableRejectedEvent(rejectedEvent)) {\n\t\t\t\tretryable.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpermanent.push(event);\n\t\t}\n\n\t\treturn { retryable, permanent };\n\t}\n\n\tprivate backoffDelayMs(attempt: number): number {\n\t\tconst rawDelay = this.retryBaseDelayMs * 2 ** attempt;\n\t\treturn Math.min(rawDelay, this.retryMaxDelayMs);\n\t}\n\n\tprivate stopTransportForAuthFailure(\n\t\tstatus: number,\n\t\trejectedCount: number,\n\t): void {\n\t\tthis.isStopped = true;\n\t\tconst buffered = this.buffer.length;\n\t\tthis.buffer.splice(0, buffered);\n\t\tthis.logger.error(\n\t\t\t\"[WaniWani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)\",\n\t\t\tstatus,\n\t\t\trejectedCount + buffered,\n\t\t);\n\t}\n}\n\nfunction isRetryableRejectedEvent(\n\trejectedEvent: V2BatchRejectedEvent,\n): boolean {\n\tif (rejectedEvent.retryable === true) return true;\n\tconst code = rejectedEvent.code.toLowerCase();\n\treturn (\n\t\tcode.includes(\"timeout\") ||\n\t\tcode.includes(\"temporary\") ||\n\t\tcode.includes(\"unavailable\") ||\n\t\tcode.includes(\"rate_limit\") ||\n\t\tcode.includes(\"transient\") ||\n\t\tcode.includes(\"server\")\n\t);\n}\n\nasync function parseJsonResponse<T>(\n\tresponse: Response,\n): Promise<T | undefined> {\n\tconst body = await response.text();\n\tif (!body) return undefined;\n\ttry {\n\t\treturn JSON.parse(body) as T;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction joinUrl(baseUrl: string, endpointPath: string): string {\n\tconst normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n\tconst normalizedPath = endpointPath.startsWith(\"/\")\n\t\t? endpointPath.slice(1)\n\t\t: endpointPath;\n\treturn `${normalizedBase}${normalizedPath}`;\n}\n\nfunction getErrorMessage(error: unknown): string {\n\tif (error instanceof Error) return error.message;\n\treturn String(error);\n}\n","// Tracking Module\n\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tTrackInput,\n\tTrackingClient,\n\tTrackingShutdownOptions,\n} from \"./@types.js\";\nimport { mapTrackEventToV2 } from \"./mapper.js\";\nimport { createV2BatchTransport } from \"./transport.js\";\n\n// Re-export types\nexport type {\n\tEventType,\n\tLegacyTrackEvent,\n\tLinkClickedProperties,\n\tPurchaseCompletedProperties,\n\tQuoteSucceededProperties,\n\tToolCalledProperties,\n\tTrackEvent,\n\tTrackInput,\n\tTrackingClient,\n\tTrackingConfig,\n\tTrackingShutdownOptions,\n\tTrackingShutdownResult,\n} from \"./@types.js\";\nexport { createEventId, mapTrackEventToV2 } from \"./mapper.js\";\nexport type {\n\tV2BatchRejectedEvent,\n\tV2BatchRequest,\n\tV2BatchResponse,\n\tV2CorrelationIds,\n\tV2EnvelopeType,\n\tV2EventEnvelope,\n} from \"./v2-types.js\";\n\nexport function createTrackingClient(config: InternalConfig): TrackingClient {\n\tconst { baseUrl, apiKey, tracking } = config;\n\n\tfunction requireApiKey(): string {\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tconst transport = apiKey\n\t\t? createV2BatchTransport({\n\t\t\t\tbaseUrl,\n\t\t\t\tapiKey,\n\t\t\t\tendpointPath: tracking.endpointPath,\n\t\t\t\tflushIntervalMs: tracking.flushIntervalMs,\n\t\t\t\tmaxBatchSize: tracking.maxBatchSize,\n\t\t\t\tmaxBufferSize: tracking.maxBufferSize,\n\t\t\t\tmaxRetries: tracking.maxRetries,\n\t\t\t\tretryBaseDelayMs: tracking.retryBaseDelayMs,\n\t\t\t\tretryMaxDelayMs: tracking.retryMaxDelayMs,\n\t\t\t\tshutdownTimeoutMs: tracking.shutdownTimeoutMs,\n\t\t\t})\n\t\t: undefined;\n\n\tconst client: TrackingClient = {\n\t\tasync track(event: TrackInput): Promise<{ eventId: string }> {\n\t\t\trequireApiKey();\n\t\t\tconst mappedEvent = mapTrackEventToV2(event);\n\t\t\ttransport?.enqueue(mappedEvent);\n\t\t\treturn { eventId: mappedEvent.id };\n\t\t},\n\t\tasync flush(): Promise<void> {\n\t\t\trequireApiKey();\n\t\t\tawait transport?.flush();\n\t\t},\n\t\tasync shutdown(options?: TrackingShutdownOptions) {\n\t\t\trequireApiKey();\n\t\t\treturn (\n\t\t\t\t(await transport?.shutdown({\n\t\t\t\t\ttimeoutMs: options?.timeoutMs ?? tracking.shutdownTimeoutMs,\n\t\t\t\t})) ?? { timedOut: false, pendingEvents: 0 }\n\t\t\t);\n\t\t},\n\t};\n\n\tif (transport) {\n\t\tattachShutdownHooks(client, tracking.shutdownTimeoutMs);\n\t}\n\treturn client;\n}\n\nfunction attachShutdownHooks(\n\tclient: TrackingClient,\n\tdefaultTimeoutMs: number,\n): void {\n\tif (\n\t\ttypeof process === \"undefined\" ||\n\t\ttypeof process.once !== \"function\" ||\n\t\ttypeof process.on !== \"function\"\n\t) {\n\t\treturn;\n\t}\n\n\tconst shutdown = () => {\n\t\tvoid client.shutdown({ timeoutMs: defaultTimeoutMs });\n\t};\n\n\tprocess.once(\"beforeExit\", shutdown);\n\tprocess.once(\"SIGINT\", shutdown);\n\tprocess.once(\"SIGTERM\", shutdown);\n}\n","// WaniWani SDK - Main Entry\n\nimport { createKbClient } from \"./kb/client.js\";\nimport { createTrackingClient } from \"./tracking/index.js\";\nimport type { WaniWaniClient, WaniWaniConfig } from \"./types.js\";\n\n/**\n * Create a WaniWani SDK client\n *\n * @param config - Configuration options\n * @returns A fully typed WaniWani client\n *\n * @example\n * ```typescript\n * import { waniwani } from \"@waniwani/sdk\";\n * import { toNextJsHandler } from \"@waniwani/sdk/next-js\";\n *\n * const wani = waniwani({ apiKey: \"...\" });\n *\n * // Next.js route handler\n * export const { GET, POST } = toNextJsHandler(wani, {\n * chat: { systemPrompt: \"You are a helpful assistant.\" },\n * });\n * ```\n */\nexport function waniwani(config?: WaniWaniConfig): WaniWaniClient {\n\tconst baseUrl = config?.baseUrl ?? \"https://app.waniwani.ai\";\n\tconst apiKey = config?.apiKey ?? process.env.WANIWANI_API_KEY;\n\tconst trackingConfig = {\n\t\tendpointPath: config?.tracking?.endpointPath ?? \"/api/mcp/events/v2/batch\",\n\t\tflushIntervalMs: config?.tracking?.flushIntervalMs ?? 1_000,\n\t\tmaxBatchSize: config?.tracking?.maxBatchSize ?? 20,\n\t\tmaxBufferSize: config?.tracking?.maxBufferSize ?? 1_000,\n\t\tmaxRetries: config?.tracking?.maxRetries ?? 3,\n\t\tretryBaseDelayMs: config?.tracking?.retryBaseDelayMs ?? 200,\n\t\tretryMaxDelayMs: config?.tracking?.retryMaxDelayMs ?? 2_000,\n\t\tshutdownTimeoutMs: config?.tracking?.shutdownTimeoutMs ?? 2_000,\n\t};\n\n\tconst internalConfig = { baseUrl, apiKey, tracking: trackingConfig };\n\n\t// Compose client from modules\n\tconst trackingClient = createTrackingClient(internalConfig);\n\tconst kbClient = createKbClient(internalConfig);\n\n\treturn {\n\t\t...trackingClient,\n\t\tkb: kbClient,\n\t\t_config: internalConfig,\n\t};\n}\n"],"mappings":"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","names":["WaniWaniError","message","status","SDK_NAME","createKbClient","config","baseUrl","apiKey","requireApiKey","request","method","path","body","key","url","headers","init","response","text","WaniWaniError","files","query","options","DEFAULT_SOURCE","mapTrackEventToV2","input","options","now","generateId","createEventId","eventName","resolveEventName","meta","toRecord","metadata","correlation","resolveCorrelationIds","eventId","takeNonEmptyString","timestamp","normalizeTimestamp","source","rawLegacy","isLegacyTrackEvent","mappedMetadata","mapProperties","legacyProperties","mapLegacyProperties","explicitProperties","properties","requestId","pickFirstString","sessionId","traceId","externalUserId","correlationId","date","record","keys","key","value","DEFAULT_ENDPOINT_PATH","SDK_NAME","AUTH_FAILURE_STATUS","RETRYABLE_STATUS","createV2BatchTransport","options","BatchingV2Transport","joinUrl","DEFAULT_ENDPOINT_PATH","delayMs","resolve","event","dropCount","timeoutMs","flushPromise","timeoutSignal","batch","attempt","pendingBatch","result","events","response","error","getErrorMessage","data","parseJsonResponse","partial","rejected","byId","retryable","permanent","rejectedEvent","isRetryableRejectedEvent","rawDelay","status","rejectedCount","buffered","code","body","baseUrl","endpointPath","normalizedBase","normalizedPath","createTrackingClient","config","baseUrl","apiKey","tracking","requireApiKey","transport","createV2BatchTransport","client","event","mappedEvent","mapTrackEventToV2","options","attachShutdownHooks","defaultTimeoutMs","shutdown","waniwani","config","baseUrl","apiKey","trackingConfig","internalConfig","trackingClient","createTrackingClient","kbClient","createKbClient"]}
@@ -1,50 +1,79 @@
1
- interface Chunk {
2
- id: string;
1
+ interface SearchResult {
3
2
  source: string;
4
3
  heading: string;
5
4
  content: string;
6
- embedding: number[];
5
+ score: number;
6
+ }
7
+ /** A file to ingest into the knowledge base */
8
+ interface KbIngestFile {
9
+ /** Filename (used as chunk source identifier) */
10
+ filename: string;
11
+ /** Markdown content of the file */
12
+ content: string;
7
13
  }
8
- interface EmbeddingsFile {
9
- model: string;
10
- dimensions?: number;
11
- generatedAt: string;
12
- chunks: Chunk[];
14
+ /** Response from the ingest endpoint */
15
+ interface KbIngestResult {
16
+ /** Number of chunks created from the ingested files */
17
+ chunksIngested: number;
18
+ /** Number of files successfully processed */
19
+ filesProcessed: number;
13
20
  }
14
- interface SearchResult {
21
+ /** Options for the search method */
22
+ interface KbSearchOptions {
23
+ /** Number of results to return (1-20, default 5) */
24
+ topK?: number;
25
+ /** Minimum similarity score threshold (0-1, default 0.3) */
26
+ minScore?: number;
27
+ }
28
+ /** A source entry in the knowledge base */
29
+ interface KbSource {
30
+ /** Source filename */
15
31
  source: string;
16
- heading: string;
17
- content: string;
18
- score: number;
32
+ /** Number of chunks from this source */
33
+ chunkCount: number;
34
+ /** ISO timestamp of when the source was first ingested */
35
+ createdAt: string;
19
36
  }
20
- interface GenerateEmbeddingsOptions {
21
- /** Directory containing .md files to embed */
22
- knowledgeDir: string;
23
- /** Output path for embeddings.json */
24
- outputPath: string;
25
- /** Embedding model string (default: "openai/text-embedding-3-small") */
26
- model?: string;
27
- /** Embedding dimensions (default: 512) */
28
- dimensions?: number;
29
- }
30
- interface KnowledgeBase {
37
+ /** KB client for server-side knowledge base operations */
38
+ interface KbClient {
39
+ /**
40
+ * Ingest files into the knowledge base.
41
+ *
42
+ * **Warning**: This is destructive it deletes ALL existing chunks
43
+ * for the environment before ingesting the new files.
44
+ */
45
+ ingest(files: KbIngestFile[]): Promise<KbIngestResult>;
31
46
  /** Search the knowledge base for relevant chunks */
32
- search(query: string, topK?: number): Promise<SearchResult[]>;
33
- /** Number of chunks in the knowledge base */
34
- readonly chunkCount: number;
47
+ search(query: string, options?: KbSearchOptions): Promise<SearchResult[]>;
48
+ /** List all sources in the knowledge base */
49
+ sources(): Promise<KbSource[]>;
35
50
  }
36
51
 
37
- declare function chunkMarkdown(filename: string, content: string): Omit<Chunk, "embedding">[];
38
- declare function generateEmbeddings(options: GenerateEmbeddingsOptions): Promise<void>;
52
+ interface TrackingConfig {
53
+ /** Events API V2 endpoint path. */
54
+ endpointPath?: string;
55
+ /** Periodic flush interval for buffered events. */
56
+ flushIntervalMs?: number;
57
+ /** Max events per HTTP batch send. */
58
+ maxBatchSize?: number;
59
+ /** Max in-memory buffer size before oldest items are dropped. */
60
+ maxBufferSize?: number;
61
+ /** Number of retries for retryable failures. */
62
+ maxRetries?: number;
63
+ /** Retry backoff base delay. */
64
+ retryBaseDelayMs?: number;
65
+ /** Retry backoff max delay. */
66
+ retryMaxDelayMs?: number;
67
+ /** Default shutdown timeout when none is provided. */
68
+ shutdownTimeoutMs?: number;
69
+ }
70
+
71
+ interface InternalConfig {
72
+ baseUrl: string;
73
+ apiKey: string | undefined;
74
+ tracking: Required<TrackingConfig>;
75
+ }
39
76
 
40
- /**
41
- * Create a knowledge base instance from pre-loaded embeddings data.
42
- *
43
- * ```typescript
44
- * import embeddings from "./embeddings.json";
45
- * const kb = loadKnowledgeBase(embeddings);
46
- * ```
47
- */
48
- declare function loadKnowledgeBase(source: EmbeddingsFile): KnowledgeBase;
77
+ declare function createKbClient(config: InternalConfig): KbClient;
49
78
 
50
- export { type Chunk, type EmbeddingsFile, type GenerateEmbeddingsOptions, type KnowledgeBase, type SearchResult, chunkMarkdown, generateEmbeddings, loadKnowledgeBase };
79
+ export { type KbClient, type KbIngestFile, type KbIngestResult, type KbSearchOptions, type KbSource, type SearchResult, createKbClient };
package/dist/kb/index.js CHANGED
@@ -1,11 +1,2 @@
1
- import{readdir as b,readFile as f,writeFile as k}from"fs/promises";import{join as $}from"path";import{embedMany as w}from"ai";var E="openai/text-embedding-3-small",y=512;function p(o,d){let a=d.split(`
2
- `),i=[],s=d.match(/^# (.+)$/m)?.[1]??o.replace(".md",""),n=s,r=[],c=0;for(let e of a){if(e.match(/^# /))continue;let m=e.match(/^## (.+)$/);if(m){if(r.length>0){let g=r.join(`
3
- `).trim();g&&(i.push({id:`${o.replace(".md","")}#${c}`,source:o,heading:n,content:`${s}: ${n}
4
-
5
- ${g}`}),c++)}n=m[1],r=[]}else r.push(e)}let t=r.join(`
6
- `).trim();return t&&i.push({id:`${o.replace(".md","")}#${c}`,source:o,heading:n,content:`${s}: ${n}
7
-
8
- ${t}`}),i}async function O(o){let{knowledgeDir:d,outputPath:a,model:i=E,dimensions:l=y}=o,s=(await b(d)).filter(e=>e.endsWith(".md"));console.log(`Found ${s.length} knowledge files`);let n=[];for(let e of s){let m=await f($(d,e),"utf-8"),g=p(e,m);n.push(...g),console.log(` ${e}: ${g.length} chunk(s)`)}console.log(`
9
- Total chunks: ${n.length}`),console.log(`Generating embeddings with ${i}...`);let{embeddings:r}=await w({model:i,values:n.map(e=>e.content),providerOptions:{openai:{dimensions:l}}}),c=n.map((e,m)=>({...e,embedding:r[m]})),t={model:i,dimensions:l,generatedAt:new Date().toISOString(),chunks:c};await k(a,JSON.stringify(t)),console.log(`
10
- Written ${a} (${c.length} chunks)`)}import{cosineSimilarity as F,embed as x}from"ai";function C(o){function d(){return o}return{get chunkCount(){return d().chunks.length},async search(a,i=5){let{model:l,dimensions:s,chunks:n}=d(),{embedding:r}=await x({model:l,value:a,...s&&{providerOptions:{openai:{dimensions:s}}}});return n.map(t=>({source:t.source,heading:t.heading,content:t.content,score:F(r,t.embedding)})).sort((t,e)=>e.score-t.score).slice(0,i)}}}export{p as chunkMarkdown,O as generateEmbeddings,C as loadKnowledgeBase};
1
+ var n=class extends Error{constructor(r,c){super(r);this.status=c;this.name="WaniWaniError"}};var f="@waniwani/sdk";function h(o){let{baseUrl:a,apiKey:r}=o;function c(){if(!r)throw new Error("WANIWANI_API_KEY is not set");return r}async function s(e,i,u){let g=c(),m=`${a.replace(/\/$/,"")}${i}`,p={Authorization:`Bearer ${g}`,"X-WaniWani-SDK":f},b={method:e,headers:p};u!==void 0&&(p["Content-Type"]="application/json",b.body=JSON.stringify(u));let t=await fetch(m,b);if(!t.ok){let S=await t.text().catch(()=>"");throw new n(S||`KB API error: HTTP ${t.status}`,t.status)}return t.json()}return{async ingest(e){return s("POST","/api/mcp/kb/ingest",{files:e})},async search(e,i){return s("POST","/api/mcp/kb/search",{query:e,...i})},async sources(){return s("GET","/api/mcp/kb/sources")}}}export{h as createKbClient};
11
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/kb/embed.ts","../../src/kb/search.ts"],"sourcesContent":["import { readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { embedMany } from \"ai\";\nimport type { Chunk, EmbeddingsFile, GenerateEmbeddingsOptions } from \"./types\";\n\nconst DEFAULT_MODEL = \"openai/text-embedding-3-small\";\nconst DEFAULT_DIMENSIONS = 512;\n\nexport function chunkMarkdown(\n\tfilename: string,\n\tcontent: string,\n): Omit<Chunk, \"embedding\">[] {\n\tconst lines = content.split(\"\\n\");\n\tconst chunks: Omit<Chunk, \"embedding\">[] = [];\n\n\tconst h1Match = content.match(/^# (.+)$/m);\n\tconst title = h1Match?.[1] ?? filename.replace(\".md\", \"\");\n\n\tlet currentHeading = title;\n\tlet currentContent: string[] = [];\n\tlet chunkIndex = 0;\n\n\tfor (const line of lines) {\n\t\tif (line.match(/^# /)) continue;\n\n\t\tconst h2Match = line.match(/^## (.+)$/);\n\t\tif (h2Match) {\n\t\t\tif (currentContent.length > 0) {\n\t\t\t\tconst text = currentContent.join(\"\\n\").trim();\n\t\t\t\tif (text) {\n\t\t\t\t\tchunks.push({\n\t\t\t\t\t\tid: `${filename.replace(\".md\", \"\")}#${chunkIndex}`,\n\t\t\t\t\t\tsource: filename,\n\t\t\t\t\t\theading: currentHeading,\n\t\t\t\t\t\tcontent: `${title}: ${currentHeading}\\n\\n${text}`,\n\t\t\t\t\t});\n\t\t\t\t\tchunkIndex++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrentHeading = h2Match[1];\n\t\t\tcurrentContent = [];\n\t\t} else {\n\t\t\tcurrentContent.push(line);\n\t\t}\n\t}\n\n\tconst text = currentContent.join(\"\\n\").trim();\n\tif (text) {\n\t\tchunks.push({\n\t\t\tid: `${filename.replace(\".md\", \"\")}#${chunkIndex}`,\n\t\t\tsource: filename,\n\t\t\theading: currentHeading,\n\t\t\tcontent: `${title}: ${currentHeading}\\n\\n${text}`,\n\t\t});\n\t}\n\n\treturn chunks;\n}\n\nexport async function generateEmbeddings(\n\toptions: GenerateEmbeddingsOptions,\n): Promise<void> {\n\tconst {\n\t\tknowledgeDir,\n\t\toutputPath,\n\t\tmodel = DEFAULT_MODEL,\n\t\tdimensions = DEFAULT_DIMENSIONS,\n\t} = options;\n\n\tconst files = (await readdir(knowledgeDir)).filter((f) => f.endsWith(\".md\"));\n\tconsole.log(`Found ${files.length} knowledge files`);\n\n\tconst allChunks: Omit<Chunk, \"embedding\">[] = [];\n\tfor (const file of files) {\n\t\tconst content = await readFile(join(knowledgeDir, file), \"utf-8\");\n\t\tconst chunks = chunkMarkdown(file, content);\n\t\tallChunks.push(...chunks);\n\t\tconsole.log(` ${file}: ${chunks.length} chunk(s)`);\n\t}\n\n\tconsole.log(`\\nTotal chunks: ${allChunks.length}`);\n\tconsole.log(`Generating embeddings with ${model}...`);\n\n\tconst { embeddings } = await embedMany({\n\t\tmodel,\n\t\tvalues: allChunks.map((c) => c.content),\n\t\tproviderOptions: { openai: { dimensions } },\n\t});\n\n\tconst chunks: Chunk[] = allChunks.map((chunk, i) => ({\n\t\t...chunk,\n\t\tembedding: embeddings[i],\n\t}));\n\n\tconst output: EmbeddingsFile = {\n\t\tmodel,\n\t\tdimensions,\n\t\tgeneratedAt: new Date().toISOString(),\n\t\tchunks,\n\t};\n\n\tawait writeFile(outputPath, JSON.stringify(output));\n\tconsole.log(`\\nWritten ${outputPath} (${chunks.length} chunks)`);\n}\n","import { cosineSimilarity, embed } from \"ai\";\nimport type { EmbeddingsFile, KnowledgeBase, SearchResult } from \"./types\";\n\n/**\n * Create a knowledge base instance from pre-loaded embeddings data.\n *\n * ```typescript\n * import embeddings from \"./embeddings.json\";\n * const kb = loadKnowledgeBase(embeddings);\n * ```\n */\nexport function loadKnowledgeBase(source: EmbeddingsFile): KnowledgeBase {\n\tfunction getEmbeddings(): EmbeddingsFile {\n\t\treturn source;\n\t}\n\n\treturn {\n\t\tget chunkCount() {\n\t\t\treturn getEmbeddings().chunks.length;\n\t\t},\n\n\t\tasync search(query: string, topK = 5): Promise<SearchResult[]> {\n\t\t\tconst { model, dimensions, chunks } = getEmbeddings();\n\n\t\t\tconst { embedding: queryEmbedding } = await embed({\n\t\t\t\tmodel,\n\t\t\t\tvalue: query,\n\t\t\t\t...(dimensions && {\n\t\t\t\t\tproviderOptions: { openai: { dimensions } },\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tconst scored = chunks.map((chunk) => ({\n\t\t\t\tsource: chunk.source,\n\t\t\t\theading: chunk.heading,\n\t\t\t\tcontent: chunk.content,\n\t\t\t\tscore: cosineSimilarity(queryEmbedding, chunk.embedding),\n\t\t\t}));\n\n\t\t\treturn scored.sort((a, b) => b.score - a.score).slice(0, topK);\n\t\t},\n\t};\n}\n"],"mappings":"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","names":["readdir","readFile","writeFile","join","embedMany","DEFAULT_MODEL","DEFAULT_DIMENSIONS","chunkMarkdown","filename","content","lines","chunks","title","currentHeading","currentContent","chunkIndex","line","h2Match","text","generateEmbeddings","options","knowledgeDir","outputPath","model","dimensions","files","f","allChunks","file","embeddings","c","chunk","i","output","cosineSimilarity","embed","loadKnowledgeBase","source","getEmbeddings","query","topK","model","dimensions","chunks","queryEmbedding","chunk","a","b"]}
1
+ {"version":3,"sources":["../../src/error.ts","../../src/kb/client.ts"],"sourcesContent":["// WaniWani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","// KB Client thin HTTP wrapper for knowledge base API\n\nimport { WaniWaniError } from \"../error.js\";\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tKbClient,\n\tKbIngestFile,\n\tKbIngestResult,\n\tKbSearchOptions,\n\tKbSource,\n\tSearchResult,\n} from \"./types.js\";\n\nconst SDK_NAME = \"@waniwani/sdk\";\n\nexport function createKbClient(config: InternalConfig): KbClient {\n\tconst { baseUrl, apiKey } = config;\n\n\tfunction requireApiKey(): string {\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tasync function request<T>(\n\t\tmethod: \"GET\" | \"POST\",\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst key = requireApiKey();\n\t\tconst url = `${baseUrl.replace(/\\/$/, \"\")}${path}`;\n\n\t\tconst headers: Record<string, string> = {\n\t\t\tAuthorization: `Bearer ${key}`,\n\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t};\n\n\t\tconst init: RequestInit = { method, headers };\n\n\t\tif (body !== undefined) {\n\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\t\t\tinit.body = JSON.stringify(body);\n\t\t}\n\n\t\tconst response = await fetch(url, init);\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\tthrow new WaniWaniError(\n\t\t\t\ttext || `KB API error: HTTP ${response.status}`,\n\t\t\t\tresponse.status,\n\t\t\t);\n\t\t}\n\n\t\treturn response.json() as Promise<T>;\n\t}\n\n\treturn {\n\t\tasync ingest(files: KbIngestFile[]): Promise<KbIngestResult> {\n\t\t\treturn request<KbIngestResult>(\"POST\", \"/api/mcp/kb/ingest\", {\n\t\t\t\tfiles,\n\t\t\t});\n\t\t},\n\n\t\tasync search(\n\t\t\tquery: string,\n\t\t\toptions?: KbSearchOptions,\n\t\t): Promise<SearchResult[]> {\n\t\t\treturn request<SearchResult[]>(\"POST\", \"/api/mcp/kb/search\", {\n\t\t\t\tquery,\n\t\t\t\t...options,\n\t\t\t});\n\t\t},\n\n\t\tasync sources(): Promise<KbSource[]> {\n\t\t\treturn request<KbSource[]>(\"GET\", \"/api/mcp/kb/sources\");\n\t\t},\n\t};\n}\n"],"mappings":"AAEO,IAAMA,EAAN,cAA4B,KAAM,CACxC,YACCC,EACOC,EACN,CACD,MAAMD,CAAO,EAFN,YAAAC,EAGP,KAAK,KAAO,eACb,CACD,ECGA,IAAMC,EAAW,gBAEV,SAASC,EAAeC,EAAkC,CAChE,GAAM,CAAE,QAAAC,EAAS,OAAAC,CAAO,EAAIF,EAE5B,SAASG,GAAwB,CAChC,GAAI,CAACD,EACJ,MAAM,IAAI,MAAM,6BAA6B,EAE9C,OAAOA,CACR,CAEA,eAAeE,EACdC,EACAC,EACAC,EACa,CACb,IAAMC,EAAML,EAAc,EACpBM,EAAM,GAAGR,EAAQ,QAAQ,MAAO,EAAE,CAAC,GAAGK,CAAI,GAE1CI,EAAkC,CACvC,cAAe,UAAUF,CAAG,GAC5B,iBAAkBV,CACnB,EAEMa,EAAoB,CAAE,OAAAN,EAAQ,QAAAK,CAAQ,EAExCH,IAAS,SACZG,EAAQ,cAAc,EAAI,mBAC1BC,EAAK,KAAO,KAAK,UAAUJ,CAAI,GAGhC,IAAMK,EAAW,MAAM,MAAMH,EAAKE,CAAI,EAEtC,GAAI,CAACC,EAAS,GAAI,CACjB,IAAMC,EAAO,MAAMD,EAAS,KAAK,EAAE,MAAM,IAAM,EAAE,EACjD,MAAM,IAAIE,EACTD,GAAQ,sBAAsBD,EAAS,MAAM,GAC7CA,EAAS,MACV,CACD,CAEA,OAAOA,EAAS,KAAK,CACtB,CAEA,MAAO,CACN,MAAM,OAAOG,EAAgD,CAC5D,OAAOX,EAAwB,OAAQ,qBAAsB,CAC5D,MAAAW,CACD,CAAC,CACF,EAEA,MAAM,OACLC,EACAC,EAC0B,CAC1B,OAAOb,EAAwB,OAAQ,qBAAsB,CAC5D,MAAAY,EACA,GAAGC,CACJ,CAAC,CACF,EAEA,MAAM,SAA+B,CACpC,OAAOb,EAAoB,MAAO,qBAAqB,CACxD,CACD,CACD","names":["WaniWaniError","message","status","SDK_NAME","createKbClient","config","baseUrl","apiKey","requireApiKey","request","method","path","body","key","url","headers","init","response","text","WaniWaniError","files","query","options"]}