@waniwani/sdk 0.15.1 → 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.
- package/dist/chat/embed.js +62 -62
- package/dist/chat/embed.js.map +1 -1
- package/dist/chat/express-js/index.d.ts +20 -14
- package/dist/chat/index.d.ts +259 -0
- package/dist/chat/index.js +7 -7
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/next-js/index.d.ts +20 -14
- package/dist/chunk-GYLVCP7M.js +3 -0
- package/dist/chunk-GYLVCP7M.js.map +1 -0
- package/dist/index.d.ts +91 -15
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy/chat/express-js/index.d.ts +20 -14
- package/dist/legacy/chat/next-js/index.d.ts +20 -14
- package/dist/legacy/index.d.ts +42 -14
- package/dist/legacy/index.js +11 -11
- package/dist/legacy/index.js.map +1 -1
- package/dist/mcp/index.d.ts +46 -20
- package/dist/mcp/index.js +5 -5
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/react.d.ts +286 -43
- package/dist/mcp/react.js +7 -7
- package/dist/mcp/react.js.map +1 -1
- package/dist/widget-client-4MBLDRHD.js +3 -0
- package/dist/widget-client-4MBLDRHD.js.map +1 -0
- package/package.json +1 -1
|
@@ -62,7 +62,14 @@ interface KbClient {
|
|
|
62
62
|
sources(): Promise<KbSource[]>;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
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
|
|
@@ -136,8 +143,6 @@ interface LeadQualifiedProperties {
|
|
|
136
143
|
email?: string;
|
|
137
144
|
/** The lead's name, if known. */
|
|
138
145
|
name?: string;
|
|
139
|
-
/** Acquisition source of the lead (e.g. "newsletter"). */
|
|
140
|
-
source?: string;
|
|
141
146
|
}
|
|
142
147
|
interface ConvertedProperties {
|
|
143
148
|
amount: number;
|
|
@@ -162,6 +167,13 @@ interface TrackingContext {
|
|
|
162
167
|
requestId?: string;
|
|
163
168
|
correlationId?: string;
|
|
164
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;
|
|
165
177
|
/** Optional explicit envelope fields. */
|
|
166
178
|
eventId?: string;
|
|
167
179
|
timestamp?: string | Date;
|
|
@@ -197,6 +209,8 @@ type TrackEvent = ({
|
|
|
197
209
|
properties?: PurchaseCompletedProperties;
|
|
198
210
|
} & BaseTrackEvent) | ({
|
|
199
211
|
event: "user.identified";
|
|
212
|
+
} & BaseTrackEvent) | ({
|
|
213
|
+
event: "widget_render";
|
|
200
214
|
} & BaseTrackEvent) | ({
|
|
201
215
|
event: "price_shown";
|
|
202
216
|
properties?: PriceShownProperties;
|
|
@@ -238,10 +252,9 @@ interface RevenuePricesComparedInput extends TrackingContext, PricesComparedProp
|
|
|
238
252
|
interface RevenueOptionSelectedInput extends TrackingContext, OptionSelectedProperties {
|
|
239
253
|
}
|
|
240
254
|
/**
|
|
241
|
-
* Input for `track.leadQualified()
|
|
242
|
-
*
|
|
243
|
-
* envelope `source`
|
|
244
|
-
* on a lead, use the generic `track({ event: "lead_qualified", … })`.
|
|
255
|
+
* Input for `track.leadQualified()` — identity only (`externalId`, `email`,
|
|
256
|
+
* `name`) plus the shared tracking context. There is no acquisition-source
|
|
257
|
+
* property; the envelope `source` (the origin channel) is set automatically.
|
|
245
258
|
*/
|
|
246
259
|
interface RevenueLeadQualifiedInput extends TrackingContext, LeadQualifiedProperties {
|
|
247
260
|
}
|
|
@@ -265,13 +278,6 @@ interface RevenueTrackingApi {
|
|
|
265
278
|
leadQualified: (input?: RevenueLeadQualifiedInput) => Promise<{
|
|
266
279
|
eventId: string;
|
|
267
280
|
}>;
|
|
268
|
-
/**
|
|
269
|
-
* @deprecated Renamed in 0.15.0. Use `track.leadQualified()` instead; this
|
|
270
|
-
* alias emits a `lead_qualified` event and will be removed in 0.16.0.
|
|
271
|
-
*/
|
|
272
|
-
lead: (input?: RevenueLeadQualifiedInput) => Promise<{
|
|
273
|
-
eventId: string;
|
|
274
|
-
}>;
|
|
275
281
|
converted: (input: RevenueConvertedInput) => Promise<{
|
|
276
282
|
eventId: string;
|
|
277
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
|
-
|
|
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
|
|
@@ -183,8 +190,6 @@ interface LeadQualifiedProperties {
|
|
|
183
190
|
email?: string;
|
|
184
191
|
/** The lead's name, if known. */
|
|
185
192
|
name?: string;
|
|
186
|
-
/** Acquisition source of the lead (e.g. "newsletter"). */
|
|
187
|
-
source?: string;
|
|
188
193
|
}
|
|
189
194
|
interface ConvertedProperties {
|
|
190
195
|
amount: number;
|
|
@@ -209,6 +214,13 @@ interface TrackingContext {
|
|
|
209
214
|
requestId?: string;
|
|
210
215
|
correlationId?: string;
|
|
211
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;
|
|
212
224
|
/** Optional explicit envelope fields. */
|
|
213
225
|
eventId?: string;
|
|
214
226
|
timestamp?: string | Date;
|
|
@@ -244,6 +256,8 @@ type TrackEvent = ({
|
|
|
244
256
|
properties?: PurchaseCompletedProperties;
|
|
245
257
|
} & BaseTrackEvent) | ({
|
|
246
258
|
event: "user.identified";
|
|
259
|
+
} & BaseTrackEvent) | ({
|
|
260
|
+
event: "widget_render";
|
|
247
261
|
} & BaseTrackEvent) | ({
|
|
248
262
|
event: "price_shown";
|
|
249
263
|
properties?: PriceShownProperties;
|
|
@@ -285,10 +299,9 @@ interface RevenuePricesComparedInput extends TrackingContext, PricesComparedProp
|
|
|
285
299
|
interface RevenueOptionSelectedInput extends TrackingContext, OptionSelectedProperties {
|
|
286
300
|
}
|
|
287
301
|
/**
|
|
288
|
-
* Input for `track.leadQualified()
|
|
289
|
-
*
|
|
290
|
-
* envelope `source`
|
|
291
|
-
* on a lead, use the generic `track({ event: "lead_qualified", … })`.
|
|
302
|
+
* Input for `track.leadQualified()` — identity only (`externalId`, `email`,
|
|
303
|
+
* `name`) plus the shared tracking context. There is no acquisition-source
|
|
304
|
+
* property; the envelope `source` (the origin channel) is set automatically.
|
|
292
305
|
*/
|
|
293
306
|
interface RevenueLeadQualifiedInput extends TrackingContext, LeadQualifiedProperties {
|
|
294
307
|
}
|
|
@@ -312,13 +325,6 @@ interface RevenueTrackingApi {
|
|
|
312
325
|
leadQualified: (input?: RevenueLeadQualifiedInput) => Promise<{
|
|
313
326
|
eventId: string;
|
|
314
327
|
}>;
|
|
315
|
-
/**
|
|
316
|
-
* @deprecated Renamed in 0.15.0. Use `track.leadQualified()` instead; this
|
|
317
|
-
* alias emits a `lead_qualified` event and will be removed in 0.16.0.
|
|
318
|
-
*/
|
|
319
|
-
lead: (input?: RevenueLeadQualifiedInput) => Promise<{
|
|
320
|
-
eventId: string;
|
|
321
|
-
}>;
|
|
322
328
|
converted: (input: RevenueConvertedInput) => Promise<{
|
|
323
329
|
eventId: string;
|
|
324
330
|
}>;
|
|
@@ -387,6 +393,74 @@ interface TrackingClient {
|
|
|
387
393
|
shutdown: (options?: TrackingShutdownOptions) => Promise<TrackingShutdownResult>;
|
|
388
394
|
}
|
|
389
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
|
+
|
|
390
464
|
interface WaniWaniConfig {
|
|
391
465
|
/**
|
|
392
466
|
* Your MCP environment API key
|
|
@@ -439,6 +513,8 @@ interface V2CorrelationIds {
|
|
|
439
513
|
requestId?: string;
|
|
440
514
|
correlationId?: string;
|
|
441
515
|
externalUserId?: string;
|
|
516
|
+
/** Anonymous visitor id; accepted as identity by the ingest API. */
|
|
517
|
+
visitorId?: string;
|
|
442
518
|
}
|
|
443
519
|
interface V2EventEnvelope {
|
|
444
520
|
id: string;
|
|
@@ -493,4 +569,4 @@ interface V2BatchResponse {
|
|
|
493
569
|
*/
|
|
494
570
|
declare function waniwani(config?: WaniWaniConfig | WaniWaniProjectConfig): WaniWaniClient;
|
|
495
571
|
|
|
496
|
-
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,source:o,...a}=t??{};return e({event:"lead_qualified",properties:{externalId:r,email:i,name:s,source:o},...a})};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
|