@waniwani/sdk 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -62,7 +62,14 @@ interface KbClient {
62
62
  sources(): Promise<KbSource[]>;
63
63
  }
64
64
 
65
- type EventType = "session.started" | "page.viewed" | "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" | "user.identified" | "price_shown" | "prices_compared" | "option_selected" | "lead_qualified" | "converted";
65
+ /**
66
+ * Every event name in the typed taxonomy, as a runtime list. Single source of
67
+ * truth for {@link EventType} and for surfaces that need to recognize
68
+ * first-class names at runtime (the widget transport passes these through
69
+ * verbatim instead of namespacing them as custom widget events).
70
+ */
71
+ declare const EVENT_TYPES: readonly ["session.started", "page.viewed", "tool.called", "quote.requested", "quote.succeeded", "quote.failed", "link.clicked", "purchase.completed", "widget_render", "user.identified", "price_shown", "prices_compared", "option_selected", "lead_qualified", "converted"];
72
+ type EventType = (typeof EVENT_TYPES)[number];
66
73
  /**
67
74
  * Properties for `page.viewed` — emitted once when the chat widget initializes
68
75
  * on a host page (the top of the funnel). Attributed to the anonymous
@@ -160,6 +167,13 @@ interface TrackingContext {
160
167
  requestId?: string;
161
168
  correlationId?: string;
162
169
  externalUserId?: string;
170
+ /**
171
+ * Anonymous visitor id (the analytics "device id"). Counts as identity on
172
+ * its own: the ingest API accepts `sessionId`, `externalUserId`, or
173
+ * `visitorId`. The chat widget uses it for pre-session events like
174
+ * `page.viewed`.
175
+ */
176
+ visitorId?: string;
163
177
  /** Optional explicit envelope fields. */
164
178
  eventId?: string;
165
179
  timestamp?: string | Date;
@@ -195,6 +209,8 @@ type TrackEvent = ({
195
209
  properties?: PurchaseCompletedProperties;
196
210
  } & BaseTrackEvent) | ({
197
211
  event: "user.identified";
212
+ } & BaseTrackEvent) | ({
213
+ event: "widget_render";
198
214
  } & BaseTrackEvent) | ({
199
215
  event: "price_shown";
200
216
  properties?: PriceShownProperties;
@@ -262,13 +278,6 @@ interface RevenueTrackingApi {
262
278
  leadQualified: (input?: RevenueLeadQualifiedInput) => Promise<{
263
279
  eventId: string;
264
280
  }>;
265
- /**
266
- * @deprecated Renamed in 0.15.0. Use `track.leadQualified()` instead; this
267
- * alias emits a `lead_qualified` event and will be removed in 0.16.0.
268
- */
269
- lead: (input?: RevenueLeadQualifiedInput) => Promise<{
270
- eventId: string;
271
- }>;
272
281
  converted: (input: RevenueConvertedInput) => Promise<{
273
282
  eventId: string;
274
283
  }>;
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ async function l(){let{detectPlatform:o}=await import("./platform-LKQFC3AJ.js");if(o()==="openai"){let{OpenAIWidgetClient:e}=await import("./openai-client-PUBKF5TI.js");return new e}else{let{MCPAppsWidgetClient:e}=await import("./mcp-apps-client-WEXKWHUI.js");return new e}}export{l as a};
3
+ //# sourceMappingURL=chunk-GYLVCP7M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/legacy/mcp/react/widgets/widget-client.ts"],"sourcesContent":["import type { CallToolResult as McpCallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { ModelContextUpdate } from \"../../../../shared/model-context\";\nimport type {\n\tDisplayMode,\n\tSafeArea,\n\tTheme,\n\tUnknownObject,\n} from \"../hooks/@types\";\n\n/**\n * Result from calling a tool\n */\nexport type ToolCallResult = McpCallToolResult;\n\n/**\n * Tool result notification (what the host pushes to the widget)\n */\nexport type ToolResult = McpCallToolResult;\n\n/**\n * Host context - all values available from the host.\n */\nexport type HostContext = {\n\ttheme: Theme;\n\tlocale: string;\n\tdisplayMode: DisplayMode;\n\tmaxHeight: number | null;\n\tsafeArea: SafeArea | null;\n\ttoolOutput: UnknownObject | null;\n\ttoolResponseMetadata: UnknownObject | null;\n\twidgetState: UnknownObject | null;\n};\n\n/**\n * Store interface for useSyncExternalStore compatibility.\n */\nexport type HostContextStore<K extends keyof HostContext> = {\n\tsubscribe: (onStoreChange: () => void) => () => void;\n\tgetSnapshot: () => HostContext[K];\n};\n\n/**\n * Unified widget client interface that works on both OpenAI and MCP Apps.\n *\n * Platform-specific behavior:\n * - Display mode: OpenAI-only. MCP Apps returns \"inline\" and requestDisplayMode is a no-op.\n * - Follow-up messages: Unified API, different underlying implementations.\n */\nexport interface UnifiedWidgetClient {\n\t/**\n\t * Connect to the host. Must be called before using other methods.\n\t * On OpenAI, this is a no-op (already connected via window.openai).\n\t * On MCP Apps, this establishes the postMessage connection.\n\t */\n\tconnect(): Promise<void>;\n\n\t/**\n\t * Close the connection to the host and clean up resources.\n\t * On OpenAI, this is a no-op.\n\t * On MCP Apps, this closes the transport and removes event listeners.\n\t */\n\tclose(): Promise<void>;\n\n\t/**\n\t * Get the tool output (structured content returned by the tool handler).\n\t * This is the main data source for widget rendering.\n\t */\n\tgetToolOutput<T = Record<string, unknown>>(): T | null;\n\n\t/**\n\t * Register a callback for when tool results are received.\n\t * On OpenAI, this subscribes to toolOutput changes.\n\t * On MCP Apps, this sets app.ontoolresult.\n\t */\n\tonToolResult(callback: (result: ToolResult) => void): () => void;\n\n\t/**\n\t * Call another tool on the server.\n\t */\n\tcallTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<ToolCallResult>;\n\n\t/**\n\t * Open an external URL.\n\t * On OpenAI: openai.openExternal({ href })\n\t * On MCP Apps: app.sendOpenLink(url)\n\t */\n\topenExternal(url: string): void;\n\n\t/**\n\t * Send a follow-up message to the AI.\n\t * On OpenAI: openai.sendFollowUpMessage({ prompt })\n\t * On MCP Apps: app.sendMessages([{ role: 'user', content: { type: 'text', text: prompt } }])\n\t */\n\tsendFollowUp(prompt: string): void | Promise<void>;\n\n\t/**\n\t * Update hidden model context for the next assistant turn.\n\t * On MCP Apps this uses the standard `ui/update-model-context` request.\n\t * On other hosts this may fall back to best-effort behavior.\n\t */\n\tupdateModelContext(context: ModelContextUpdate): Promise<void> | void;\n\n\t/**\n\t * Get the current theme.\n\t */\n\tgetTheme(): Theme;\n\n\t/**\n\t * Subscribe to theme changes.\n\t */\n\tonThemeChange(callback: (theme: Theme) => void): () => void;\n\n\t/**\n\t * Get the current locale.\n\t */\n\tgetLocale(): string;\n\n\t/**\n\t * Get the current display mode.\n\t * OpenAI-only: returns \"pip\" | \"inline\" | \"fullscreen\"\n\t * MCP Apps: always returns \"inline\"\n\t */\n\tgetDisplayMode(): DisplayMode;\n\n\t/**\n\t * Request a display mode change.\n\t * OpenAI-only: requests the mode from the host.\n\t * MCP Apps: no-op (returns current mode).\n\t */\n\trequestDisplayMode(mode: DisplayMode): Promise<DisplayMode>;\n\n\t/**\n\t * Subscribe to display mode changes.\n\t * OpenAI-only: subscribes to displayMode changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonDisplayModeChange(callback: (mode: DisplayMode) => void): () => void;\n\n\t/**\n\t * Get the safe area insets.\n\t * OpenAI-only: returns insets from window.openai.safeArea.\n\t * MCP Apps: returns null.\n\t */\n\tgetSafeArea(): SafeArea | null;\n\n\t/**\n\t * Subscribe to safe area changes.\n\t * OpenAI-only: subscribes to safeArea changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonSafeAreaChange(callback: (safeArea: SafeArea | null) => void): () => void;\n\n\t/**\n\t * Get the max height constraint.\n\t * OpenAI-only: returns maxHeight from window.openai.maxHeight.\n\t * MCP Apps: returns null.\n\t */\n\tgetMaxHeight(): number | null;\n\n\t/**\n\t * Subscribe to max height changes.\n\t * OpenAI-only: subscribes to maxHeight changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonMaxHeightChange(callback: (maxHeight: number | null) => void): () => void;\n\n\t/**\n\t * Get the tool response metadata.\n\t * OpenAI: returns metadata from window.openai.toolResponseMetadata.\n\t * MCP Apps: returns `_meta` from the latest `ui/notifications/tool-result`, if provided by host.\n\t */\n\tgetToolResponseMetadata(): UnknownObject | null;\n\n\t/**\n\t * Subscribe to tool response metadata changes.\n\t * OpenAI: subscribes to toolResponseMetadata changes.\n\t * MCP Apps: fires when tool result `_meta` changes.\n\t */\n\tonToolResponseMetadataChange(\n\t\tcallback: (metadata: UnknownObject | null) => void,\n\t): () => void;\n\n\t/**\n\t * Get the widget state.\n\t * OpenAI-only: returns state from window.openai.widgetState.\n\t * MCP Apps: returns null.\n\t */\n\tgetWidgetState(): UnknownObject | null;\n\n\t/**\n\t * Subscribe to widget state changes.\n\t * OpenAI-only: subscribes to widgetState changes.\n\t * MCP Apps: callback is never called.\n\t */\n\tonWidgetStateChange(\n\t\tcallback: (state: UnknownObject | null) => void,\n\t): () => void;\n}\n\n/**\n * Creates a unified widget client for the current platform.\n */\nexport async function createWidgetClient(): Promise<UnifiedWidgetClient> {\n\tconst { detectPlatform } = await import(\"./platform\");\n\tconst platform = detectPlatform();\n\n\tif (platform === \"openai\") {\n\t\tconst { OpenAIWidgetClient } = await import(\"./openai-client\");\n\t\treturn new OpenAIWidgetClient();\n\t} else {\n\t\tconst { MCPAppsWidgetClient } = await import(\"./mcp-apps-client\");\n\t\treturn new MCPAppsWidgetClient();\n\t}\n}\n"],"mappings":";AA6MA,eAAsBA,GAAmD,CACxE,GAAM,CAAE,eAAAC,CAAe,EAAI,KAAM,QAAO,wBAAY,EAGpD,GAFiBA,EAAe,IAEf,SAAU,CAC1B,GAAM,CAAE,mBAAAC,CAAmB,EAAI,KAAM,QAAO,6BAAiB,EAC7D,OAAO,IAAIA,CACZ,KAAO,CACN,GAAM,CAAE,oBAAAC,CAAoB,EAAI,KAAM,QAAO,+BAAmB,EAChE,OAAO,IAAIA,CACZ,CACD","names":["createWidgetClient","detectPlatform","OpenAIWidgetClient","MCPAppsWidgetClient"]}
package/dist/index.d.ts CHANGED
@@ -109,7 +109,14 @@ interface WaniWaniProjectConfig {
109
109
  */
110
110
  declare function defineConfig(config: WaniWaniProjectConfig): WaniWaniProjectConfig;
111
111
 
112
- type EventType = "session.started" | "page.viewed" | "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" | "user.identified" | "price_shown" | "prices_compared" | "option_selected" | "lead_qualified" | "converted";
112
+ /**
113
+ * Every event name in the typed taxonomy, as a runtime list. Single source of
114
+ * truth for {@link EventType} and for surfaces that need to recognize
115
+ * first-class names at runtime (the widget transport passes these through
116
+ * verbatim instead of namespacing them as custom widget events).
117
+ */
118
+ declare const EVENT_TYPES: readonly ["session.started", "page.viewed", "tool.called", "quote.requested", "quote.succeeded", "quote.failed", "link.clicked", "purchase.completed", "widget_render", "user.identified", "price_shown", "prices_compared", "option_selected", "lead_qualified", "converted"];
119
+ type EventType = (typeof EVENT_TYPES)[number];
113
120
  /**
114
121
  * Properties for `page.viewed` — emitted once when the chat widget initializes
115
122
  * on a host page (the top of the funnel). Attributed to the anonymous
@@ -207,6 +214,13 @@ interface TrackingContext {
207
214
  requestId?: string;
208
215
  correlationId?: string;
209
216
  externalUserId?: string;
217
+ /**
218
+ * Anonymous visitor id (the analytics "device id"). Counts as identity on
219
+ * its own: the ingest API accepts `sessionId`, `externalUserId`, or
220
+ * `visitorId`. The chat widget uses it for pre-session events like
221
+ * `page.viewed`.
222
+ */
223
+ visitorId?: string;
210
224
  /** Optional explicit envelope fields. */
211
225
  eventId?: string;
212
226
  timestamp?: string | Date;
@@ -242,6 +256,8 @@ type TrackEvent = ({
242
256
  properties?: PurchaseCompletedProperties;
243
257
  } & BaseTrackEvent) | ({
244
258
  event: "user.identified";
259
+ } & BaseTrackEvent) | ({
260
+ event: "widget_render";
245
261
  } & BaseTrackEvent) | ({
246
262
  event: "price_shown";
247
263
  properties?: PriceShownProperties;
@@ -309,13 +325,6 @@ interface RevenueTrackingApi {
309
325
  leadQualified: (input?: RevenueLeadQualifiedInput) => Promise<{
310
326
  eventId: string;
311
327
  }>;
312
- /**
313
- * @deprecated Renamed in 0.15.0. Use `track.leadQualified()` instead; this
314
- * alias emits a `lead_qualified` event and will be removed in 0.16.0.
315
- */
316
- lead: (input?: RevenueLeadQualifiedInput) => Promise<{
317
- eventId: string;
318
- }>;
319
328
  converted: (input: RevenueConvertedInput) => Promise<{
320
329
  eventId: string;
321
330
  }>;
@@ -384,6 +393,74 @@ interface TrackingClient {
384
393
  shutdown: (options?: TrackingShutdownOptions) => Promise<TrackingShutdownResult>;
385
394
  }
386
395
 
396
+ /**
397
+ * Correlation identity read at emit time, so identifiers that only exist
398
+ * later (a server-assigned chat session id, for example) are picked up on
399
+ * every event without re-creating the client.
400
+ */
401
+ interface FrontendIdentity {
402
+ sessionId?: string;
403
+ visitorId?: string;
404
+ externalUserId?: string;
405
+ traceId?: string;
406
+ }
407
+ interface FrontendClientOptions {
408
+ /**
409
+ * Full URL of the events ingest endpoint
410
+ * (e.g. `https://app.waniwani.ai/api/mcp/events/v2/batch`).
411
+ */
412
+ endpoint: string;
413
+ /**
414
+ * Browser-safe credential: an environment public token (`wwp_...`) or a
415
+ * widget JWT injected by `withWaniwani` into tool response `_meta`. Omit
416
+ * only when the endpoint is your own unauthenticated proxy route.
417
+ */
418
+ token?: string;
419
+ /**
420
+ * Channel attribution stamped on every event (e.g. `"chatgpt"`, `"web"`).
421
+ * The ingest API rejects events it cannot attribute to a channel, so set
422
+ * this (or `channelId`) unless every event carries its own. Accepts a
423
+ * getter for values that resolve after client creation.
424
+ */
425
+ source?: string | (() => string | undefined);
426
+ /**
427
+ * Exact channel attribution, stamped as `properties.channelId` on every
428
+ * event that does not set its own. Accepts a getter for values that
429
+ * resolve after client creation.
430
+ */
431
+ channelId?: string | (() => string | undefined);
432
+ /** Live identity, called on every emit. */
433
+ identity?: () => FrontendIdentity;
434
+ /** Extra fields merged into every event's envelope metadata. */
435
+ metadata?: Record<string, unknown>;
436
+ /** Periodic flush interval for buffered events. */
437
+ flushIntervalMs?: number;
438
+ }
439
+ /**
440
+ * The browser counterpart of the server tracking client: the exact same
441
+ * `track` surface (callable plus the flat revenue helpers) over the same
442
+ * batching transport, with identity stamped automatically.
443
+ */
444
+ interface FrontendTrackingClient {
445
+ track: TrackFn;
446
+ /**
447
+ * Attach a stable external user id. Emits `user.identified` and stamps
448
+ * `externalUserId` on every subsequent event from this client.
449
+ */
450
+ identify(userId: string, properties?: Record<string, unknown>): Promise<{
451
+ eventId: string;
452
+ }>;
453
+ flush(): Promise<void>;
454
+ shutdown(options?: TrackingShutdownOptions): Promise<TrackingShutdownResult>;
455
+ }
456
+ /**
457
+ * Create a tracking client for browser surfaces (MCP-app widgets, the chat
458
+ * embed, or any page holding a `wwp_` public token). Events flush in batches,
459
+ * survive page navigation via keepalive teardown, and never throw into the
460
+ * host page.
461
+ */
462
+ declare function createFrontendClient(options: FrontendClientOptions): FrontendTrackingClient;
463
+
387
464
  interface WaniWaniConfig {
388
465
  /**
389
466
  * Your MCP environment API key
@@ -436,6 +513,8 @@ interface V2CorrelationIds {
436
513
  requestId?: string;
437
514
  correlationId?: string;
438
515
  externalUserId?: string;
516
+ /** Anonymous visitor id; accepted as identity by the ingest API. */
517
+ visitorId?: string;
439
518
  }
440
519
  interface V2EventEnvelope {
441
520
  id: string;
@@ -490,4 +569,4 @@ interface V2BatchResponse {
490
569
  */
491
570
  declare function waniwani(config?: WaniWaniConfig | WaniWaniProjectConfig): WaniWaniClient;
492
571
 
493
- export { type ComparedPriceOption, type ConvertedProperties, type EventType, type KbClient, type KbIngestFile, type KbIngestResult, type KbSearchOptions, type KbSource, type LeadQualifiedProperties, type LegacyTrackEvent, type LinkClickedProperties, type OptionSelectedProperties, type PriceShownProperties, type PricesComparedProperties, type PurchaseCompletedProperties, type QuoteSucceededProperties, type RevenueConvertedInput, type RevenueLeadQualifiedInput, type RevenueOptionSelectedInput, type RevenuePriceShownInput, type RevenuePricesComparedInput, type RevenueTrackingApi, type ToolCalledProperties, type TrackEvent, type TrackFn, 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, type WaniWaniProjectConfig, defineConfig, waniwani };
572
+ export { type ComparedPriceOption, type ConvertedProperties, EVENT_TYPES, type EventType, type FrontendClientOptions, type FrontendIdentity, type FrontendTrackingClient, type KbClient, type KbIngestFile, type KbIngestResult, type KbSearchOptions, type KbSource, type LeadQualifiedProperties, type LegacyTrackEvent, type LinkClickedProperties, type OptionSelectedProperties, type PriceShownProperties, type PricesComparedProperties, type PurchaseCompletedProperties, type QuoteSucceededProperties, type RevenueConvertedInput, type RevenueLeadQualifiedInput, type RevenueOptionSelectedInput, type RevenuePriceShownInput, type RevenuePricesComparedInput, type RevenueTrackingApi, type ToolCalledProperties, type TrackEvent, type TrackFn, 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, type WaniWaniProjectConfig, createFrontendClient, defineConfig, waniwani };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var y=class extends Error{constructor(t,r){super(t);this.status=r;this.name="WaniWaniError"}};import{existsSync as N,readFileSync as q}from"fs";import{resolve as z}from"path";var Y="waniwani.json",m;function R(){if(m!==void 0)return m;try{let e=z(process.cwd(),Y);if(!N(e))return m=null,null;let n=q(e,"utf-8");return m=JSON.parse(n),m}catch{return m=null,null}}var b="__waniwani_config__";function $(e){return globalThis[b]=e,e}function x(){return globalThis[b]}import{AsyncLocalStorage as H}from"async_hooks";var C=globalThis,T=C.__waniwaniKbRetrievalStore;T||(T=new H,C.__waniwaniKbRetrievalStore=T);var X=T;function _(e){try{X.getStore()?.searches.push(e)}catch{}}var Q="@waniwani/sdk";function P(e){let{apiUrl:n,apiKey:t}=e;function r(){if(!t)throw new Error("WANIWANI_API_KEY is not set");return t}async function i(s,o,a){let p=r(),g=`${n.replace(/\/$/,"")}${o}`,u={Authorization:`Bearer ${p}`,"X-WaniWani-SDK":Q},c={method:s,headers:u};a!==void 0&&(u["Content-Type"]="application/json",c.body=JSON.stringify(a));let l=await fetch(g,c);if(!l.ok){let V=await l.text().catch(()=>"");throw new y(V||`KB API error: HTTP ${l.status}`,l.status)}return(await l.json()).data}return{async ingest(s){return i("POST","/api/mcp/kb/ingest",{files:s})},async search(s,o){let a=await i("POST","/api/mcp/kb/search",{query:s,...o});return _({query:s,resultCount:a.length,results:a.map(p=>({source:p.source,heading:p.heading,score:p.score}))}),a},async sources(){return i("GET","/api/mcp/kb/sources")}}}function v(e,n){for(let t of n){let r=e[t];if(typeof r=="string"&&r.length>0)return r}}var G=["waniwani/sessionId","openai/sessionId","openai/session","sessionId","conversationId","mcp-session-id"],J=["waniwani/requestId","openai/requestId","requestId","mcp/requestId"],Z=["waniwani/traceId","openai/traceId","traceId","mcp/traceId","openai/requestId","requestId"],ee=["waniwani/userId","openai/userId","externalUserId","userId","actorId"],te=["correlationId","openai/requestId"];function M(e){return e?v(e,G):void 0}function A(e){return e?v(e,J):void 0}function D(e){return e?v(e,Z):void 0}function W(e){return e?v(e,ee):void 0}function B(e){return e?v(e,te):void 0}var ne=[{key:"openai/sessionId",source:"chatgpt"},{key:"openai/session",source:"chatgpt"}],re=[{needle:"claude",source:"claude"},{needle:"chatgpt",source:"chatgpt"},{needle:"openai",source:"chatgpt"},{needle:"gemini",source:"gemini"}];function F(e,n){if(e){let r=e["waniwani/source"];if(typeof r=="string"&&r.length>0)return r;for(let{key:i,source:s}of ne){let o=e[i];if(typeof o=="string"&&o.length>0)return s}}let t=n?.name;if(typeof t=="string"&&t.length>0){let r=t.toLowerCase();for(let{needle:i,source:s}of re)if(r.includes(i))return s}}var ie="@waniwani/sdk";function w(e,n={}){let t=n.now??(()=>new Date),r=n.generateId??L,i=ae(e),s=I(e.meta),o=I(e.metadata),a=ce(e,s),p=d(e.eventId)??r(),g=ue(e.timestamp,t),u=d(e.source)??F(s)??n.source??ie,c=E(e)?{...e}:void 0,l={...o};return Object.keys(s).length>0&&(l.meta=s),c&&(l.rawLegacy=c),{id:p,type:"mcp.event",name:i,source:u,timestamp:g,correlation:a,properties:se(e,i),metadata:l,rawLegacy:c}}function L(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?`evt_${crypto.randomUUID()}`:`evt_${Math.random().toString(36).slice(2,10)}_${Date.now().toString(36)}`}function se(e,n){if(!E(e))return I(e.properties);let t=oe(e,n),r=I(e.properties);return{...t,...r}}function oe(e,n){switch(n){case"tool.called":{let t={};return d(e.toolName)&&(t.name=e.toolName),d(e.toolType)&&(t.type=e.toolType),t}case"quote.succeeded":{let t={};return typeof e.quoteAmount=="number"&&(t.amount=e.quoteAmount),d(e.quoteCurrency)&&(t.currency=e.quoteCurrency),t}case"link.clicked":{let t={};return d(e.linkUrl)&&(t.url=e.linkUrl),t}case"purchase.completed":{let t={};return typeof e.purchaseAmount=="number"&&(t.amount=e.purchaseAmount),d(e.purchaseCurrency)&&(t.currency=e.purchaseCurrency),t}default:return{}}}function ae(e){let n=E(e)?e.eventType:e.event;return n==="lead"?"lead_qualified":n}function ce(e,n){let t=d(e.requestId)??A(n),r=d(e.sessionId)??M(n),i=d(e.traceId)??D(n),s=d(e.externalUserId)??W(n),o=d(e.correlationId)??B(n)??t,a={};return r&&(a.sessionId=r),i&&(a.traceId=i),t&&(a.requestId=t),o&&(a.correlationId=o),s&&(a.externalUserId=s),a}function ue(e,n){if(e instanceof Date)return e.toISOString();if(typeof e=="string"){let t=new Date(e);if(!Number.isNaN(t.getTime()))return t.toISOString()}return n().toISOString()}function I(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function d(e){if(typeof e=="string"&&e.trim().length!==0)return e}function E(e){return"eventType"in e}function U(e){let n=t=>{let{externalId:r,email:i,name:s,...o}=t??{};return e({event:"lead_qualified",properties:{externalId:r,email:i,name:s},...o})};return{priceShown:({amount:t,currency:r,itemId:i,label:s,...o})=>e({event:"price_shown",properties:{amount:t,currency:r,itemId:i,label:s},...o}),pricesCompared:({options:t,...r})=>e({event:"prices_compared",properties:{options:t},...r}),optionSelected:({id:t,amount:r,currency:i,...s})=>e({event:"option_selected",properties:{id:t,amount:r,currency:i},...s}),leadQualified:n,lead:n,converted:({amount:t,currency:r,occurredAt:i,...s})=>e({event:"converted",properties:{amount:t,currency:r,occurredAt:i},...s})}}var de="/api/mcp/events/v2/batch";var j="@waniwani/sdk",pe=new Set([401,403]),le=new Set([408,425,429,500,502,503,504]);function O(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(n){this.endpointUrl=ge(n.apiUrl,n.endpointPath??de),this.flushIntervalMs=n.flushIntervalMs??1e3,this.maxBatchSize=n.maxBatchSize??20,this.maxBufferSize=n.maxBufferSize??1e3,this.maxRetries=n.maxRetries??3,this.retryBaseDelayMs=n.retryBaseDelayMs??200,this.retryMaxDelayMs=n.retryMaxDelayMs??2e3,this.shutdownTimeoutMs=n.shutdownTimeoutMs??2e3,this.fetchFn=n.fetchFn??fetch,this.logger=n.logger??console,this.now=n.now??(()=>new Date),this.sleep=n.sleep??(t=>new Promise(r=>setTimeout(r,t))),this.apiKey=n.apiKey,this.sdkVersion=n.sdkVersion,this.flushIntervalMs>0&&(this.flushTimer=setInterval(()=>{this.flush()},this.flushIntervalMs))}enqueue(n){if(this.isStopped||this.isShuttingDown){this.logger.warn("[Waniwani] Tracking transport is stopped, dropping event %s",n.id);return}if(this.buffer.length>=this.maxBufferSize){let t=this.buffer.length-this.maxBufferSize+1;this.buffer.splice(0,t),this.logger.warn("[Waniwani] Tracking buffer overflow, dropped %d oldest event(s)",t)}if(this.buffer.push(n),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(n){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 t=n?.timeoutMs??this.shutdownTimeoutMs,r=this.flush();if(!Number.isFinite(t)||t<=0)return await r,this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()};let i=Symbol("shutdown-timeout");return await Promise.race([r.then(()=>"flushed"),this.sleep(t).then(()=>i)])===i?(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 n=this.buffer.splice(0,this.maxBatchSize);await this.sendBatchWithRetry(n)}}async sendBatchWithRetry(n){let t=0,r=n;for(;r.length>0&&!this.isStopped;){this.inFlightCount=r.length;let i=await this.sendBatchOnce(r);switch(this.inFlightCount=0,i.kind){case"success":return;case"auth":this.stopTransportForAuthFailure(i.status,r.length);return;case"permanent":this.logger.error("[Waniwani] Dropping %d event(s) after permanent failure: %s",r.length,i.reason);return;case"retryable":if(t>=this.maxRetries){this.logger.error("[Waniwani] Dropping %d event(s) after retry exhaustion: %s",r.length,i.reason);return}await this.sleep(this.backoffDelayMs(t)),t+=1;continue;case"partial":if(i.permanent.length>0&&this.logger.error("[Waniwani] Dropping %d event(s) rejected as permanent",i.permanent.length),i.retryable.length===0)return;if(t>=this.maxRetries){this.logger.error("[Waniwani] Dropping %d retryable event(s) after retry exhaustion",i.retryable.length);return}r=i.retryable,await this.sleep(this.backoffDelayMs(t)),t+=1;continue}}}async sendBatchOnce(n){let t;try{t=await this.fetchFn(this.endpointUrl,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,"X-WaniWani-SDK":j},body:JSON.stringify(this.makeBatchRequest(n))})}catch(s){return{kind:"retryable",reason:me(s)}}if(pe.has(t.status))return{kind:"auth",status:t.status};if(le.has(t.status))return{kind:"retryable",reason:`HTTP ${t.status}`};if(!t.ok)return{kind:"permanent",reason:`HTTP ${t.status}`};let r=await he(t);if(!r?.rejected||r.rejected.length===0)return{kind:"success"};let i=this.classifyRejectedEvents(n,r.rejected);return i.retryable.length===0&&i.permanent.length===0?{kind:"success"}:{kind:"partial",retryable:i.retryable,permanent:i.permanent}}makeBatchRequest(n){return{sentAt:this.now().toISOString(),source:{sdk:j,version:this.sdkVersion??"0.0.0"},events:n}}classifyRejectedEvents(n,t){let r=new Map(n.map(o=>[o.id,o])),i=[],s=[];for(let o of t){let a=r.get(o.eventId);if(a){if(fe(o)){i.push(a);continue}s.push(a)}}return{retryable:i,permanent:s}}backoffDelayMs(n){let t=this.retryBaseDelayMs*2**n;return Math.min(t,this.retryMaxDelayMs)}stopTransportForAuthFailure(n,t){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)",n,t+r)}};function fe(e){if(e.retryable===!0)return!0;let n=e.code.toLowerCase();return n.includes("timeout")||n.includes("temporary")||n.includes("unavailable")||n.includes("rate_limit")||n.includes("transient")||n.includes("server")}async function he(e){let n=await e.text();if(n)try{return JSON.parse(n)}catch{return}}function ge(e,n){let t=e.endsWith("/")?e:`${e}/`,r=n.startsWith("/")?n.slice(1):n;return`${t}${r}`}function me(e){return e instanceof Error?e.message:String(e)}function K(e){let{apiUrl:n,apiKey:t,tracking:r}=e;function i(){if(!t)throw new Error("WANIWANI_API_KEY is not set");return t}let s=t?O({apiUrl:n,apiKey:t,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;function o(u){i();let c=w(u);return!c.correlation.sessionId&&!c.correlation.externalUserId&&console.warn(`[waniwani] event "${c.name}" has no sessionId or externalUserId; the ingest API requires one and will reject it.`),s?.enqueue(c),{eventId:c.id}}let a=async u=>o(u),p=Object.assign(a,U(a)),g={async identify(u,c,l){i();let S=w({event:"user.identified",externalUserId:u,properties:c,meta:l});return s?.enqueue(S),{eventId:S.id}},track:p,async flush(){i(),await s?.flush()},async shutdown(u){return i(),await s?.shutdown({timeoutMs:u?.timeoutMs??r.shutdownTimeoutMs})??{timedOut:!1,pendingEvents:0}}};return s&&ye(g,r.shutdownTimeoutMs),g}function ye(e,n){if(typeof process>"u"||typeof process.once!="function"||typeof process.on!="function")return;let t=()=>{e.shutdown({timeoutMs:n})};process.once("beforeExit",t),process.once("SIGINT",t),process.once("SIGTERM",t)}function ve(e){let t=e??R()??x(),r=t?.apiUrl??process.env.WANIWANI_API_URL??"https://app.waniwani.ai",i=t?.apiKey??process.env.WANIWANI_API_KEY,s={endpointPath:t?.tracking?.endpointPath??"/api/mcp/events/v2/batch",flushIntervalMs:t?.tracking?.flushIntervalMs??1e3,maxBatchSize:t?.tracking?.maxBatchSize??20,maxBufferSize:t?.tracking?.maxBufferSize??1e3,maxRetries:t?.tracking?.maxRetries??3,retryBaseDelayMs:t?.tracking?.retryBaseDelayMs??200,retryMaxDelayMs:t?.tracking?.retryMaxDelayMs??2e3,shutdownTimeoutMs:t?.tracking?.shutdownTimeoutMs??2e3},o={apiUrl:r,apiKey:i,tracking:s},a=K(o),p=P(o);return{...a,kb:p,_config:o}}export{y as WaniWaniError,$ as defineConfig,ve as waniwani};
1
+ var T=class extends Error{constructor(n,r){super(n);this.status=r;this.name="WaniWaniError"}};import{existsSync as Y,readFileSync as z}from"fs";import{resolve as $}from"path";var H="waniwani.json",m;function P(){if(m!==void 0)return m;try{let e=$(process.cwd(),H);if(!Y(e))return m=null,null;let t=z(e,"utf-8");return m=JSON.parse(t),m}catch{return m=null,null}}var b="__waniwani_config__";function Q(e){return globalThis[b]=e,e}function C(){return globalThis[b]}var _=["session.started","page.viewed","tool.called","quote.requested","quote.succeeded","quote.failed","link.clicked","purchase.completed","widget_render","user.identified","price_shown","prices_compared","option_selected","lead_qualified","converted"];function v(e,t){for(let n of t){let r=e[n];if(typeof r=="string"&&r.length>0)return r}}var X=["waniwani/sessionId","openai/sessionId","openai/session","sessionId","conversationId","mcp-session-id"],G=["waniwani/requestId","openai/requestId","requestId","mcp/requestId"],J=["waniwani/traceId","openai/traceId","traceId","mcp/traceId","openai/requestId","requestId"],Z=["waniwani/userId","openai/userId","externalUserId","userId","actorId"],ee=["waniwani/visitorId","visitorId"],te=["correlationId","openai/requestId"];function M(e){return e?v(e,X):void 0}function A(e){return e?v(e,G):void 0}function B(e){return e?v(e,J):void 0}function F(e){return e?v(e,Z):void 0}function O(e){return e?v(e,ee):void 0}function D(e){return e?v(e,te):void 0}var ne=[{key:"openai/sessionId",source:"chatgpt"},{key:"openai/session",source:"chatgpt"}],re=[{needle:"claude",source:"claude"},{needle:"chatgpt",source:"chatgpt"},{needle:"openai",source:"chatgpt"},{needle:"gemini",source:"gemini"}];function L(e,t){if(e){let r=e["waniwani/source"];if(typeof r=="string"&&r.length>0)return r;for(let{key:i,source:s}of ne){let a=e[i];if(typeof a=="string"&&a.length>0)return s}}let n=t?.name;if(typeof n=="string"&&n.length>0){let r=n.toLowerCase();for(let{needle:i,source:s}of re)if(r.includes(i))return s}}var ie="@waniwani/sdk";function y(e,t={}){let n=t.now??(()=>new Date),r=t.generateId??U,i=ae(e),s=I(e.meta),a=I(e.metadata),o=ce(e,s),c=p(e.eventId)??r(),g=ue(e.timestamp,n),d=p(e.source)??L(s)??t.source??ie,u=x(e)?{...e}:void 0,h={...a};return Object.keys(s).length>0&&(h.meta=s),u&&(h.rawLegacy=u),{id:c,type:"mcp.event",name:i,source:d,timestamp:g,correlation:o,properties:se(e,i),metadata:h,rawLegacy:u}}function U(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?`evt_${crypto.randomUUID()}`:`evt_${Math.random().toString(36).slice(2,10)}_${Date.now().toString(36)}`}function se(e,t){if(!x(e))return I(e.properties);let n=oe(e,t),r=I(e.properties);return{...n,...r}}function oe(e,t){switch(t){case"tool.called":{let n={};return p(e.toolName)&&(n.name=e.toolName),p(e.toolType)&&(n.type=e.toolType),n}case"quote.succeeded":{let n={};return typeof e.quoteAmount=="number"&&(n.amount=e.quoteAmount),p(e.quoteCurrency)&&(n.currency=e.quoteCurrency),n}case"link.clicked":{let n={};return p(e.linkUrl)&&(n.url=e.linkUrl),n}case"purchase.completed":{let n={};return typeof e.purchaseAmount=="number"&&(n.amount=e.purchaseAmount),p(e.purchaseCurrency)&&(n.currency=e.purchaseCurrency),n}default:return{}}}function ae(e){return x(e)?e.eventType:e.event}function ce(e,t){let n=p(e.requestId)??A(t),r=p(e.sessionId)??M(t),i=p(e.traceId)??B(t),s=p(e.externalUserId)??F(t),a=p(e.visitorId)??O(t),o=p(e.correlationId)??D(t)??n,c={};return r&&(c.sessionId=r),i&&(c.traceId=i),n&&(c.requestId=n),o&&(c.correlationId=o),s&&(c.externalUserId=s),a&&(c.visitorId=a),c}function ue(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 I(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function p(e){if(typeof e=="string"&&e.trim().length!==0)return e}function x(e){return"eventType"in e}function k(e){return{priceShown:({amount:t,currency:n,itemId:r,label:i,...s})=>e({event:"price_shown",properties:{amount:t,currency:n,itemId:r,label:i},...s}),pricesCompared:({options:t,...n})=>e({event:"prices_compared",properties:{options:t},...n}),optionSelected:({id:t,amount:n,currency:r,...i})=>e({event:"option_selected",properties:{id:t,amount:n,currency:r},...i}),leadQualified:t=>{let{externalId:n,email:r,name:i,...s}=t??{};return e({event:"lead_qualified",properties:{externalId:n,email:r,name:i},...s})},converted:({amount:t,currency:n,occurredAt:r,...i})=>e({event:"converted",properties:{amount:t,currency:n,occurredAt:r},...i})}}var de="/api/mcp/events/v2/batch";var W="@waniwani/sdk";var pe=new Set([401,403]),le=new Set([408,425,429,500,502,503,504]);function w(e){return new R(e)}var R=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=ge(t.apiUrl,t.endpointPath??de),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)),this.registerBrowserTeardown()}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 i=Symbol("shutdown-timeout");return await Promise.race([r.then(()=>"flushed"),this.sleep(n).then(()=>i)])===i?(this.isStopped=!0,{timedOut:!0,pendingEvents:this.pendingEvents()}):(this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()})}registerBrowserTeardown(){if(typeof document>"u"||typeof window>"u")return;let t=()=>{document.visibilityState==="hidden"&&this.keepaliveFlush()};document.addEventListener("visibilitychange",t),window.addEventListener("pagehide",()=>this.keepaliveFlush())}keepaliveFlush(){if(this.isStopped||this.buffer.length===0)return;let t=this.buffer.splice(0,this.buffer.length);this.sendKeepaliveChunk(t)}sendKeepaliveChunk(t){let n=JSON.stringify(this.makeBatchRequest(t));if(n.length>6e4&&t.length>1){let r=Math.ceil(t.length/2);this.sendKeepaliveChunk(t.slice(0,r)),this.sendKeepaliveChunk(t.slice(r));return}try{this.fetchFn(this.endpointUrl,{method:"POST",headers:this.requestHeaders(),body:n,keepalive:!0}).catch(()=>{})}catch{}}requestHeaders(){let t={"Content-Type":"application/json","X-WaniWani-SDK":W};return this.apiKey&&(t.Authorization=`Bearer ${this.apiKey}`),t}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 i=await this.sendBatchOnce(r);switch(this.inFlightCount=0,i.kind){case"success":return;case"auth":this.stopTransportForAuthFailure(i.status,r.length);return;case"permanent":this.logger.error("[Waniwani] Dropping %d event(s) after permanent failure: %s",r.length,i.reason);return;case"retryable":if(n>=this.maxRetries){this.logger.error("[Waniwani] Dropping %d event(s) after retry exhaustion: %s",r.length,i.reason);return}await this.sleep(this.backoffDelayMs(n)),n+=1;continue;case"partial":if(i.permanent.length>0&&this.logger.error("[Waniwani] Dropping %d event(s) rejected as permanent",i.permanent.length),i.retryable.length===0)return;if(n>=this.maxRetries){this.logger.error("[Waniwani] Dropping %d retryable event(s) after retry exhaustion",i.retryable.length);return}r=i.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:this.requestHeaders(),body:JSON.stringify(this.makeBatchRequest(t))})}catch(s){return{kind:"retryable",reason:me(s)}}if(pe.has(n.status))return{kind:"auth",status:n.status};if(le.has(n.status))return{kind:"retryable",reason:`HTTP ${n.status}`};if(!n.ok)return{kind:"permanent",reason:`HTTP ${n.status}`};let r=await he(n);if(!r?.rejected||r.rejected.length===0)return{kind:"success"};let i=this.classifyRejectedEvents(t,r.rejected);return i.retryable.length===0&&i.permanent.length===0?{kind:"success"}:{kind:"partial",retryable:i.retryable,permanent:i.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])),i=[],s=[];for(let a of n){let o=r.get(a.eventId);if(o){if(fe(a)){i.push(o);continue}s.push(o)}}return{retryable:i,permanent:s}}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 fe(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 he(e){let t=await e.text();if(t)try{return JSON.parse(t)}catch{return}}function ge(e,t){let n=e.endsWith("/")?e:`${e}/`,r=t.startsWith("/")?t.slice(1):t;return`${n}${r}`}function me(e){return e instanceof Error?e.message:String(e)}function ve(e){let{apiUrl:t,endpointPath:n}=ye(e.endpoint),r=w({apiUrl:t,apiKey:e.token,endpointPath:n,flushIntervalMs:e.flushIntervalMs}),i,s=o=>typeof o=="function"?o():o,a=async o=>{let c=e.identity?.()??{},g=s(e.channelId),d=g===void 0?o.properties:{channelId:g,...o.properties},u=y({sessionId:c.sessionId,visitorId:c.visitorId,externalUserId:c.externalUserId??i,traceId:c.traceId,...o,properties:d,metadata:{...e.metadata,...o.metadata}},{source:s(e.source)});return r.enqueue(u),{eventId:u.id}};return{track:Object.assign(a,k(a)),identify(o,c){return i=o,a({event:"user.identified",externalUserId:o,properties:c})},flush:()=>r.flush(),shutdown:o=>r.shutdown(o)}}function ye(e){let t=new URL(e);return{apiUrl:t.origin,endpointPath:`${t.pathname}${t.search}`}}import{AsyncLocalStorage as Te}from"async_hooks";var V=globalThis,E=V.__waniwaniKbRetrievalStore;E||(E=new Te,V.__waniwaniKbRetrievalStore=E);var Ie=E;function K(e){try{Ie.getStore()?.searches.push(e)}catch{}}var ke="@waniwani/sdk";function j(e){let{apiUrl:t,apiKey:n}=e;function r(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}async function i(s,a,o){let c=r(),g=`${t.replace(/\/$/,"")}${a}`,d={Authorization:`Bearer ${c}`,"X-WaniWani-SDK":ke},u={method:s,headers:d};o!==void 0&&(d["Content-Type"]="application/json",u.body=JSON.stringify(o));let h=await fetch(g,u);if(!h.ok){let N=await h.text().catch(()=>"");throw new T(N||`KB API error: HTTP ${h.status}`,h.status)}return(await h.json()).data}return{async ingest(s){return i("POST","/api/mcp/kb/ingest",{files:s})},async search(s,a){let o=await i("POST","/api/mcp/kb/search",{query:s,...a});return K({query:s,resultCount:o.length,results:o.map(c=>({source:c.source,heading:c.heading,score:c.score}))}),o},async sources(){return i("GET","/api/mcp/kb/sources")}}}function q(e){let{apiUrl:t,apiKey:n,tracking:r}=e;function i(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}let s=n?w({apiUrl: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;function a(d){i();let u=y(d);return!u.correlation.sessionId&&!u.correlation.externalUserId&&!u.correlation.visitorId&&console.warn(`[waniwani] event "${u.name}" has no sessionId, externalUserId, or visitorId; the ingest API requires one and will reject it.`),s?.enqueue(u),{eventId:u.id}}let o=async d=>a(d),c=Object.assign(o,k(o)),g={async identify(d,u,h){i();let S=y({event:"user.identified",externalUserId:d,properties:u,meta:h});return s?.enqueue(S),{eventId:S.id}},track:c,async flush(){i(),await s?.flush()},async shutdown(d){return i(),await s?.shutdown({timeoutMs:d?.timeoutMs??r.shutdownTimeoutMs})??{timedOut:!1,pendingEvents:0}}};return s&&we(g,r.shutdownTimeoutMs),g}function we(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 Ee(e){let n=e??P()??C(),r=n?.apiUrl??process.env.WANIWANI_API_URL??"https://app.waniwani.ai",i=n?.apiKey??process.env.WANIWANI_API_KEY,s={endpointPath:n?.tracking?.endpointPath??"/api/mcp/events/v2/batch",flushIntervalMs:n?.tracking?.flushIntervalMs??1e3,maxBatchSize:n?.tracking?.maxBatchSize??20,maxBufferSize:n?.tracking?.maxBufferSize??1e3,maxRetries:n?.tracking?.maxRetries??3,retryBaseDelayMs:n?.tracking?.retryBaseDelayMs??200,retryMaxDelayMs:n?.tracking?.retryMaxDelayMs??2e3,shutdownTimeoutMs:n?.tracking?.shutdownTimeoutMs??2e3},a={apiUrl:r,apiKey:i,tracking:s},o=q(a),c=j(a);return{...o,kb:c,_config:a}}export{_ as EVENT_TYPES,T as WaniWaniError,ve as createFrontendClient,Q as defineConfig,Ee as waniwani};
2
2
  //# sourceMappingURL=index.js.map