@nlxai/core 1.2.3-alpha.1 → 1.2.3

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/lib/index.cjs CHANGED
@@ -6,7 +6,7 @@ var ReconnectingWebSocket = require('reconnecting-websocket');
6
6
  var uuid = require('uuid');
7
7
 
8
8
  var name = "@nlxai/core";
9
- var version$1 = "1.2.3-alpha.1";
9
+ var version$1 = "1.2.3";
10
10
  var description = "Low-level SDK for building NLX experiences";
11
11
  var type = "module";
12
12
  var main = "lib/index.cjs";
@@ -22,40 +22,40 @@ var exports$1 = {
22
22
  };
23
23
  var scripts = {
24
24
  build: "rm -rf lib && rollup -c --configPlugin typescript --configImportAttributesKey with",
25
- docs: "rm -rf docs/ && typedoc && concat-md --decrease-title-levels --dir-name-as-title docs/ > docs/index.md",
26
25
  "lint:check": "eslint src/ --ext .ts,.tsx,.js,.jsx --max-warnings 0",
27
26
  lint: "eslint src/ --ext .ts,.tsx,.js,.jsx --fix",
28
27
  prepublish: "npm run build",
29
- "preview-docs": "npm run docs && comrak --unsafe --gfm -o docs/index.html docs/index.md && open docs/index.html",
30
- "publish-docs": "npm run docs && mv docs/index.md ../website/src/content/headless-api-reference.md",
31
28
  test: "typedoc --emit none",
32
- tsc: "tsc"
29
+ tsc: "tsc",
30
+ "update-readme:docs": "rm -rf docs/ && typedoc",
31
+ "update-readme:merge": "../../scripts/transclude-markdown.js",
32
+ "update-readme": "npm run update-readme:docs && npm run update-readme:merge"
33
33
  };
34
34
  var author = "Peter Szerzo <peter@nlx.ai>";
35
35
  var license = "MIT";
36
36
  var devDependencies = {
37
- "@types/isomorphic-fetch": "^0.0.36",
38
- "@types/node": "^22.10.1",
39
- "@types/ramda": "0.29.1",
37
+ "@types/isomorphic-fetch": "^0.0.39",
38
+ "@types/node": "^24.10.1",
39
+ "@types/ramda": "0.31.1",
40
40
  "@types/uuid": "^9.0.7",
41
41
  "concat-md": "^0.5.1",
42
42
  "eslint-config-nlx": "*",
43
43
  prettier: "^3.1.0",
44
44
  "rollup-config-nlx": "*",
45
- typedoc: "^0.25.13",
46
- "typedoc-plugin-markdown": "^3.17.1",
45
+ typedoc: "^0.28.14",
46
+ "typedoc-plugin-markdown": "^4.9.0",
47
47
  typescript: "^5.5.4"
48
48
  };
49
49
  var dependencies = {
50
50
  "isomorphic-fetch": "^3.0.0",
51
- ramda: "^0.29.1",
51
+ ramda: "^0.32.0",
52
52
  "reconnecting-websocket": "^4.4.0",
53
53
  uuid: "^9.0.1"
54
54
  };
55
55
  var publishConfig = {
56
56
  access: "public"
57
57
  };
58
- var gitHead = "13e26a6c67af7aa6c8dc100ad83d9c33e1112a08";
58
+ var gitHead = "3902161e95745dd4a9cfb68bb469755a62b421f5";
59
59
  var packageJson = {
60
60
  name: name,
61
61
  version: version$1,
@@ -127,18 +127,6 @@ const safeJsonParse = (val) => {
127
127
  return null;
128
128
  }
129
129
  };
130
- /**
131
- * Helper method to decide when a new {@link Config} requires creating a new {@link ConversationHandler} or whether the old `Config`'s
132
- * `ConversationHandler` can be used.
133
- *
134
- * The order of configs doesn't matter.
135
- * @param config1 -
136
- * @param config2 -
137
- * @returns true if `createConversation` should be called again
138
- */
139
- const shouldReinitialize = (config1, config2) => {
140
- return !ramda.equals(config1, config2);
141
- };
142
130
  const getBaseDomain = (url) => url.match(/(bots\.dev\.studio\.nlx\.ai|bots\.studio\.nlx\.ai|apps\.nlx\.ai|dev\.apps\.nlx\.ai)/g)?.[0] ?? "apps.nlx.ai";
143
131
  /**
144
132
  * When a HTTP URL is provided, deduce the websocket URL. Otherwise, return the argument.
@@ -175,41 +163,45 @@ const normalizeToHttp = (applicationUrl) => {
175
163
  const isWebsocketUrl = (url) => {
176
164
  return url.indexOf("wss://") === 0;
177
165
  };
178
- /**
179
- * Check whether a configuration is value.
180
- * @param config - Chat configuration
181
- * @returns isValid - Whether the configuration is valid
182
- */
183
- const isConfigValid = (config) => {
184
- const applicationUrl = config.applicationUrl ?? "";
185
- return applicationUrl.length > 0;
186
- };
187
166
  /**
188
167
  * Call this to create a conversation handler.
189
- * @param config -
168
+ * @param configuration - The necessary configuration to create the conversation.
190
169
  * @returns The {@link ConversationHandler} is a bundle of functions to interact with the conversation.
170
+ * @example
171
+ * ```typescript
172
+ * import { createConversation } from "@nlx/core";
173
+ *
174
+ * const conversation = createConversation({
175
+ * applicationUrl: "https://apps.nlx.ai/c/cfab3-243ad-232dc",
176
+ * headers: {
177
+ * "nlx-api-key": "4393029032-dwsd",
178
+ * },
179
+ * userId: "abcd-1234",
180
+ * languageCode: "en-US",
181
+ * });
182
+ * ```
191
183
  */
192
- function createConversation(config) {
184
+ function createConversation(configuration) {
193
185
  let socket;
194
186
  let socketMessageQueue = [];
195
187
  let socketMessageQueueCheckInterval = null;
196
188
  let voicePlusSocket;
197
189
  let voicePlusSocketMessageQueue = [];
198
190
  let voicePlusSocketMessageQueueCheckInterval = null;
199
- const applicationUrl = config.applicationUrl ?? "";
191
+ const applicationUrl = configuration.applicationUrl ?? "";
200
192
  // Check if the application URL has a language code appended to it
201
193
  if (/[-|_][a-z]{2,}[-|_][A-Z]{2,}$/.test(applicationUrl)) {
202
194
  Console.warn("Since v1.0.0, the language code is no longer added at the end of the application URL. Please remove the modifier (e.g. '-en-US') from the URL, and specify it in the `languageCode` parameter instead.");
203
195
  }
204
196
  const eventListeners = { voicePlusCommand: [] };
205
- const initialConversationId = config.conversationId ?? uuid.v4();
197
+ const initialConversationId = configuration.conversationId ?? uuid.v4();
206
198
  let state = {
207
- responses: config.responses ?? [],
208
- languageCode: config.languageCode,
209
- userId: config.userId,
199
+ responses: configuration.responses ?? [],
200
+ languageCode: configuration.languageCode,
201
+ userId: configuration.userId,
210
202
  conversationId: initialConversationId,
211
203
  };
212
- const fullApplicationHttpUrl = () => `${normalizeToHttp(applicationUrl)}${config.experimental?.completeApplicationUrl === true
204
+ const fullApplicationHttpUrl = () => `${normalizeToHttp(applicationUrl)}${configuration.experimental?.completeApplicationUrl === true
213
205
  ? ""
214
206
  : `-${state.languageCode}`}`;
215
207
  const setState = (change,
@@ -228,7 +220,7 @@ function createConversation(config) {
228
220
  type: exports.ResponseType.Failure,
229
221
  receivedAt: new Date().getTime(),
230
222
  payload: {
231
- text: config.failureMessage ?? defaultFailureMessage,
223
+ text: configuration.failureMessage ?? defaultFailureMessage,
232
224
  },
233
225
  };
234
226
  setState({
@@ -283,6 +275,7 @@ function createConversation(config) {
283
275
  const sendToApplication = async (body) => {
284
276
  if (requestOverride != null) {
285
277
  requestOverride(body, (payload) => {
278
+ Console.warn("Using the second argument in `setRequestOverride` is deprecated. Use `conversationHandler.appendMessageToTranscript` instead.");
286
279
  const newResponse = {
287
280
  type: exports.ResponseType.Application,
288
281
  receivedAt: new Date().getTime(),
@@ -299,8 +292,8 @@ function createConversation(config) {
299
292
  conversationId: state.conversationId,
300
293
  ...body,
301
294
  languageCode: state.languageCode,
302
- channelType: config.experimental?.channelType,
303
- environment: config.environment,
295
+ channelType: configuration.experimental?.channelType,
296
+ environment: configuration.environment,
304
297
  };
305
298
  if (isWebsocketUrl(applicationUrl)) {
306
299
  if (socket?.readyState === 1) {
@@ -315,7 +308,7 @@ function createConversation(config) {
315
308
  const res = await fetch(fullApplicationHttpUrl(), {
316
309
  method: "POST",
317
310
  headers: {
318
- ...(config.headers ?? {}),
311
+ ...(configuration.headers ?? {}),
319
312
  Accept: "application/json",
320
313
  "Content-Type": "application/json",
321
314
  "nlx-sdk-version": packageJson.version,
@@ -352,7 +345,7 @@ function createConversation(config) {
352
345
  // If the socket is already set up, tear it down first
353
346
  teardownWebsocket();
354
347
  const url = new URL(applicationUrl);
355
- if (config.experimental?.completeApplicationUrl !== true) {
348
+ if (configuration.experimental?.completeApplicationUrl !== true) {
356
349
  url.searchParams.set("languageCode", state.languageCode);
357
350
  url.searchParams.set("channelKey", `${url.searchParams.get("channelKey") ?? ""}-${state.languageCode}`);
358
351
  }
@@ -385,17 +378,17 @@ function createConversation(config) {
385
378
  const setupCommandWebsocket = () => {
386
379
  // If the socket is already set up, tear it down first
387
380
  teardownCommandWebsocket();
388
- if (config.bidirectional !== true) {
381
+ if (configuration.bidirectional !== true) {
389
382
  return;
390
383
  }
391
384
  const url = new URL(normalizeToWebsocket(applicationUrl));
392
- if (config.experimental?.completeApplicationUrl !== true) {
385
+ if (configuration.experimental?.completeApplicationUrl !== true) {
393
386
  url.searchParams.set("languageCode", state.languageCode);
394
387
  url.searchParams.set("channelKey", `${url.searchParams.get("channelKey") ?? ""}-${state.languageCode}`);
395
388
  }
396
389
  url.searchParams.set("conversationId", state.conversationId);
397
390
  url.searchParams.set("type", "voice-plus");
398
- const apiKey = config.headers["nlx-api-key"];
391
+ const apiKey = configuration.headers["nlx-api-key"];
399
392
  if (!isWebsocketUrl(applicationUrl) && apiKey != null) {
400
393
  url.searchParams.set("apiKey", apiKey);
401
394
  }
@@ -539,7 +532,7 @@ function createConversation(config) {
539
532
  const res = await fetch(`${fullApplicationHttpUrl()}/context`, {
540
533
  method: "POST",
541
534
  headers: {
542
- ...(config.headers ?? {}),
535
+ ...(configuration.headers ?? {}),
543
536
  Accept: "application/json",
544
537
  "Content-Type": "application/json",
545
538
  "nlx-conversation-id": state.conversationId,
@@ -556,6 +549,15 @@ function createConversation(config) {
556
549
  throw new Error(`Responded with ${res.status}`);
557
550
  }
558
551
  },
552
+ appendMessageToTranscript: (newResponse) => {
553
+ const newResponseWithTimestamp = {
554
+ ...newResponse,
555
+ receivedAt: newResponse.receivedAt ?? new Date().getTime(),
556
+ };
557
+ setState({
558
+ responses: [...state.responses, newResponseWithTimestamp],
559
+ }, newResponseWithTimestamp);
560
+ },
559
561
  sendStructured: (structured, context) => {
560
562
  appendStructuredUserResponse(structured, context);
561
563
  void sendToApplication({
@@ -611,7 +613,7 @@ function createConversation(config) {
611
613
  const res = await fetch(`${url}-${state.languageCode}/requestToken`, {
612
614
  method: "POST",
613
615
  headers: {
614
- ...(config.headers ?? {}),
616
+ ...(configuration.headers ?? {}),
615
617
  Accept: "application/json",
616
618
  "Content-Type": "application/json",
617
619
  "nlx-conversation-id": state.conversationId,
@@ -672,9 +674,52 @@ function createConversation(config) {
672
674
  };
673
675
  }
674
676
  /**
675
- * Get current expiration timestamp from the current list of reponses
676
- * @param responses - the current list of user and application responses (first argument in the subscribe callback)
677
- * @returns an expiration timestamp in Unix Epoch (`new Date().getTime()`), or `null` if this is not known (typically occurs if the application has not responded yet)
677
+ * Check whether a configuration is valid.
678
+ * @param configuration - Conversation configuration
679
+ * @returns Whether the configuration is valid?
680
+ */
681
+ const isConfigValid = (configuration) => {
682
+ const applicationUrl = configuration.applicationUrl ?? "";
683
+ return applicationUrl.length > 0;
684
+ };
685
+ /**
686
+ * Helper method to decide when a new {@link Config} requires creating a new {@link ConversationHandler} or whether the old `Config`'s
687
+ * `ConversationHandler` can be used.
688
+ *
689
+ * The order of configs doesn't matter.
690
+ * @param config1 -
691
+ * @param config2 -
692
+ * @returns true if `createConversation` should be called again
693
+ */
694
+ const shouldReinitialize = (config1, config2) => {
695
+ return !ramda.equals(config1, config2);
696
+ };
697
+ /**
698
+ * Get current expiration timestamp from a list of responses. Can be used to determine if a conversation has timed out.
699
+ * @example
700
+ * ```typescript
701
+ * import { useState } from "react";
702
+ * import { getCurrentExpirationTimestamp } from "@nlxai/core";
703
+ *
704
+ * const [isTimedOut, setIsTimedOut] = useState(false);
705
+ *
706
+ * conversation.subscribe((responses) => {
707
+ * const expirationTimestamp = getCurrentExpirationTimestamp(responses);
708
+ * if (expirationTimestamp != null && expirationTimestamp < new Date().getTime()) {
709
+ * setIsTimedOut(true);
710
+ * }
711
+ * });
712
+ *
713
+ * return (<div>
714
+ * {isTimedOut ? (
715
+ * <p>Your session has timed out. Please start a new conversation.</p>
716
+ * ) : (
717
+ * <p>Your session is active.</p>
718
+ * )}
719
+ * </div>
720
+ * ```
721
+ * @param responses - The current list of user and application responses (first argument in the subscribe callback)
722
+ * @returns An expiration timestamp in Unix Epoch (`new Date().getTime()`), or `null` if this is not known (typically occurs if the application has not responded yet)
678
723
  */
679
724
  const getCurrentExpirationTimestamp = (responses) => {
680
725
  let expirationTimestamp = null;
@@ -702,7 +747,7 @@ const getCurrentExpirationTimestamp = (responses) => {
702
747
  * console.log(response);
703
748
  * });
704
749
  * ```
705
- * @typeParam T - the type of the function's params, e.g. for `sendText` it's `text: string, context?: Context`
750
+ * @typeParam Params - the type of the function's params, e.g. for `sendText` it's `text: string, context?: Context`
706
751
  * @param fn - the function to wrap (e.g. `convo.sendText`, `convo.sendChoice`, etc.)
707
752
  * @param convo - the `ConversationHandler` (from {@link createConversation})
708
753
  * @param timeout - the timeout in milliseconds