@botonic/core 0.47.0 → 0.48.1

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.
@@ -0,0 +1,311 @@
1
+ import {
2
+ type BotContext,
3
+ type BotRequest,
4
+ type BotSecrets,
5
+ type BotSettings,
6
+ type ContactInfo,
7
+ INPUT,
8
+ type Input,
9
+ PROVIDER,
10
+ type ProviderType,
11
+ type ResolvedPlugins,
12
+ type Session,
13
+ type SessionUser,
14
+ } from '../models'
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Default values
18
+ // ---------------------------------------------------------------------------
19
+
20
+ export const TEST_DEFAULTS = {
21
+ HUBTYPE_API_URL: 'https://api.hubtype.com',
22
+ STATIC_URL: 'https://static.hubtype.com',
23
+ LITELLM_API_URL: 'https://api.litellm.com',
24
+ AZURE_OPENAI_API_BASE: 'https://api.openai.com',
25
+ AZURE_OPENAI_API_VERSION: '2026-02-01',
26
+ LANGUAGE_DETECTION_ENABLED: true,
27
+ HUBTYPE_ACCESS_TOKEN: 'testAccessToken', // pragma: allowlist secret
28
+ LITELLM_API_KEY: 'testLiteLLMAPIKey', // pragma: allowlist secret
29
+ AZURE_OPENAI_API_KEY: 'testAzureOpenAIAPIKey', // pragma: allowlist secret
30
+ ACCESS_TOKEN: 'fake_access_token',
31
+ HUBTYPE_API_HOST: 'https://api.hubtype.com',
32
+ FLOW_THREAD_ID: 'testFlowThreadId',
33
+ BOT_ID: 'testBotId',
34
+ USER_ID: 'testUserId',
35
+ ORG_ID: 'testOrgId',
36
+ ORG_NAME: 'testOrg',
37
+ PROVIDER: PROVIDER.WEBCHAT as ProviderType,
38
+ LOCALE: 'en',
39
+ COUNTRY: 'US',
40
+ BOT_INTERACTION_ID: 'testInteractionId',
41
+ MESSAGE_ID: 'testMessageId',
42
+ LAST_ROUTE_PATH: '',
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Leaf factories
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export function createTestSettings(
50
+ overrides?: Partial<BotSettings>
51
+ ): BotSettings {
52
+ return {
53
+ HUBTYPE_API_URL: TEST_DEFAULTS.HUBTYPE_API_URL,
54
+ STATIC_URL: TEST_DEFAULTS.STATIC_URL,
55
+ LITELLM_API_URL: TEST_DEFAULTS.LITELLM_API_URL,
56
+ AZURE_OPENAI_API_BASE: TEST_DEFAULTS.AZURE_OPENAI_API_BASE,
57
+ AZURE_OPENAI_API_VERSION: TEST_DEFAULTS.AZURE_OPENAI_API_VERSION,
58
+ LANGUAGE_DETECTION_ENABLED: TEST_DEFAULTS.LANGUAGE_DETECTION_ENABLED,
59
+ CUSTOM_SHORT_URL_HOST: null,
60
+ custom: {},
61
+ ...overrides,
62
+ }
63
+ }
64
+
65
+ export function createTestSecrets(overrides?: Partial<BotSecrets>): BotSecrets {
66
+ return {
67
+ HUBTYPE_ACCESS_TOKEN: TEST_DEFAULTS.HUBTYPE_ACCESS_TOKEN,
68
+ LITELLM_API_KEY: TEST_DEFAULTS.LITELLM_API_KEY,
69
+ AZURE_OPENAI_API_KEY: TEST_DEFAULTS.AZURE_OPENAI_API_KEY,
70
+ custom: {},
71
+ ...overrides,
72
+ }
73
+ }
74
+
75
+ export interface TestUserOptions {
76
+ id?: string
77
+ provider?: ProviderType
78
+ locale?: string
79
+ country?: string
80
+ systemLocale?: string
81
+ contactInfo?: ContactInfo[]
82
+ extraData?: Record<string, any>
83
+ }
84
+
85
+ export function createTestSessionUser(
86
+ options?: TestUserOptions
87
+ ): SessionUser<Record<string, any>> {
88
+ const locale = options?.locale ?? TEST_DEFAULTS.LOCALE
89
+ const country = options?.country ?? TEST_DEFAULTS.COUNTRY
90
+ return {
91
+ id: options?.id ?? TEST_DEFAULTS.USER_ID,
92
+ provider: options?.provider ?? TEST_DEFAULTS.PROVIDER,
93
+ locale,
94
+ country,
95
+ system_locale: options?.systemLocale ?? locale,
96
+ contact_info: options?.contactInfo ?? [],
97
+ extra_data: options?.extraData ?? {},
98
+ }
99
+ }
100
+
101
+ export interface TestSessionOptions {
102
+ user?: TestUserOptions
103
+ botId?: string
104
+ organization?: string
105
+ organizationId?: string
106
+ isFirstInteraction?: boolean
107
+ isTestIntegration?: boolean
108
+ accessToken?: string
109
+ hubtypeApi?: string
110
+ flowThreadId?: string
111
+ retries?: number
112
+ shadowing?: boolean
113
+ hubtypeCaseId?: string
114
+ captureUserInputNodeId?: string
115
+ }
116
+
117
+ export function createTestSession(
118
+ options?: TestSessionOptions
119
+ ): Session<Record<string, any>> {
120
+ return {
121
+ bot: { id: options?.botId ?? TEST_DEFAULTS.BOT_ID },
122
+ user: createTestSessionUser(options?.user),
123
+ organization: options?.organization ?? TEST_DEFAULTS.ORG_NAME,
124
+ organization_id: options?.organizationId ?? TEST_DEFAULTS.ORG_ID,
125
+ is_first_interaction: options?.isFirstInteraction ?? false,
126
+ is_test_integration: options?.isTestIntegration ?? false,
127
+ _access_token: options?.accessToken ?? TEST_DEFAULTS.ACCESS_TOKEN,
128
+ _hubtype_api: options?.hubtypeApi ?? TEST_DEFAULTS.HUBTYPE_API_HOST,
129
+ flow_thread_id: options?.flowThreadId ?? TEST_DEFAULTS.FLOW_THREAD_ID,
130
+ __retries: options?.retries ?? 0,
131
+ _shadowing: options?.shadowing,
132
+ _hubtype_case_id: options?.hubtypeCaseId,
133
+ capture_user_input: options?.captureUserInputNodeId
134
+ ? { node_id: options.captureUserInputNodeId }
135
+ : undefined,
136
+ }
137
+ }
138
+
139
+ export interface TestInputOptions {
140
+ type?: Input['type']
141
+ data?: string
142
+ text?: string
143
+ payload?: string
144
+ src?: string
145
+ botInteractionId?: string
146
+ messageId?: string
147
+ context?: Input['context']
148
+ transcript?: string
149
+ }
150
+
151
+ export function createTestInput(options?: TestInputOptions): Input {
152
+ return {
153
+ type: options?.type ?? INPUT.TEXT,
154
+ data: options?.data,
155
+ text: options?.text,
156
+ payload: options?.payload,
157
+ src: options?.src,
158
+ transcript: options?.transcript,
159
+ context: options?.context,
160
+ bot_interaction_id:
161
+ options?.botInteractionId ?? TEST_DEFAULTS.BOT_INTERACTION_ID,
162
+ message_id: options?.messageId ?? TEST_DEFAULTS.MESSAGE_ID,
163
+ }
164
+ }
165
+
166
+ // ---------------------------------------------------------------------------
167
+ // Composite factories
168
+ // ---------------------------------------------------------------------------
169
+
170
+ export interface TestBotRequestOptions {
171
+ input?: TestInputOptions | Input
172
+ session?: TestSessionOptions | Session
173
+ lastRoutePath?: string
174
+ settings?: Partial<BotSettings> | BotSettings
175
+ secrets?: Partial<BotSecrets> | BotSecrets
176
+ }
177
+
178
+ function isFullInput(v: TestInputOptions | Input): v is Input {
179
+ return 'bot_interaction_id' in v && 'message_id' in v
180
+ }
181
+
182
+ function isFullSession(v: TestSessionOptions | Session): v is Session {
183
+ return 'bot' in v && '__retries' in v
184
+ }
185
+
186
+ function isFullSettings(
187
+ v: Partial<BotSettings> | BotSettings
188
+ ): v is BotSettings {
189
+ return (
190
+ 'HUBTYPE_API_URL' in v &&
191
+ 'STATIC_URL' in v &&
192
+ 'LITELLM_API_URL' in v &&
193
+ 'AZURE_OPENAI_API_BASE' in v &&
194
+ 'AZURE_OPENAI_API_VERSION' in v &&
195
+ 'LANGUAGE_DETECTION_ENABLED' in v &&
196
+ 'CUSTOM_SHORT_URL_HOST' in v &&
197
+ 'custom' in v
198
+ )
199
+ }
200
+
201
+ function isFullSecrets(v: Partial<BotSecrets> | BotSecrets): v is BotSecrets {
202
+ return (
203
+ 'HUBTYPE_ACCESS_TOKEN' in v &&
204
+ 'LITELLM_API_KEY' in v &&
205
+ 'AZURE_OPENAI_API_KEY' in v &&
206
+ 'custom' in v
207
+ )
208
+ }
209
+
210
+ export function createTestBotRequest(
211
+ options?: TestBotRequestOptions
212
+ ): BotRequest {
213
+ const input =
214
+ options?.input == null
215
+ ? createTestInput()
216
+ : isFullInput(options.input)
217
+ ? options.input
218
+ : createTestInput(options.input)
219
+
220
+ const session =
221
+ options?.session == null
222
+ ? createTestSession()
223
+ : isFullSession(options.session)
224
+ ? options.session
225
+ : createTestSession(options.session)
226
+
227
+ const settings =
228
+ options?.settings == null
229
+ ? createTestSettings()
230
+ : isFullSettings(options.settings)
231
+ ? options.settings
232
+ : createTestSettings(options.settings)
233
+
234
+ const secrets =
235
+ options?.secrets == null
236
+ ? createTestSecrets()
237
+ : isFullSecrets(options.secrets)
238
+ ? options.secrets
239
+ : createTestSecrets(options.secrets)
240
+
241
+ return {
242
+ input,
243
+ session,
244
+ lastRoutePath: options?.lastRoutePath ?? TEST_DEFAULTS.LAST_ROUTE_PATH,
245
+ settings,
246
+ secrets,
247
+ }
248
+ }
249
+
250
+ export interface TestBotContextOptions<
251
+ TPlugins extends ResolvedPlugins = ResolvedPlugins,
252
+ > extends TestBotRequestOptions {
253
+ plugins?: TPlugins
254
+ params?: Record<string, string>
255
+ defaultDelay?: number
256
+ defaultTyping?: number
257
+ }
258
+
259
+ export function createTestBotContext<
260
+ TPlugins extends ResolvedPlugins = ResolvedPlugins,
261
+ >(options?: TestBotContextOptions<TPlugins>): BotContext<TPlugins> {
262
+ const request = createTestBotRequest(options)
263
+
264
+ const sessionForLocale =
265
+ options?.session == null
266
+ ? createTestSession()
267
+ : isFullSession(options.session)
268
+ ? options.session
269
+ : createTestSession(options.session)
270
+
271
+ const locale = sessionForLocale.user.locale
272
+ const country = sessionForLocale.user.country
273
+ const systemLocale = sessionForLocale.user.system_locale
274
+
275
+ return {
276
+ ...request,
277
+ plugins: options?.plugins ?? ({} as TPlugins),
278
+ params: options?.params ?? {},
279
+ defaultDelay: options?.defaultDelay ?? 0,
280
+ defaultTyping: options?.defaultTyping ?? 0,
281
+ getUserLocale: () => locale,
282
+ getUserCountry: () => country,
283
+ getSystemLocale: () => systemLocale,
284
+ setUserLocale: () => undefined,
285
+ setUserCountry: () => undefined,
286
+ setSystemLocale: () => undefined,
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Alias for `createTestBotContext` typed as `PluginPreRequest`.
292
+ * PluginPreRequest === BotContext in @botonic/core.
293
+ */
294
+ export const createTestPluginPreRequest = createTestBotContext
295
+
296
+ export interface TestPluginPostRequestOptions<
297
+ TPlugins extends ResolvedPlugins = ResolvedPlugins,
298
+ > extends TestBotContextOptions<TPlugins> {
299
+ response?: string | null
300
+ }
301
+
302
+ export function createTestPluginPostRequest<
303
+ TPlugins extends ResolvedPlugins = ResolvedPlugins,
304
+ >(
305
+ options?: TestPluginPostRequestOptions<TPlugins>
306
+ ): BotContext<TPlugins> & { response: string | null } {
307
+ return {
308
+ ...createTestBotContext(options),
309
+ response: options?.response ?? null,
310
+ }
311
+ }