@miso.ai/client-sdk-core 1.13.0-beta.8 → 1.13.1-beta.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.
@@ -69,7 +69,7 @@ class Ask extends _base.default {
69
69
  exports.default = Ask;
70
70
  class Answer extends _iterable.IdBasedIterableApiStub {
71
71
  constructor(api, response, options = {}) {
72
- super(api, '_questionGet', response.question_id, options);
72
+ super(api, '_questionGet', response.question_id, mergeCustomApiIterator(api, options));
73
73
  this._response = response;
74
74
  }
75
75
  get questionId() {
@@ -78,7 +78,7 @@ class Answer extends _iterable.IdBasedIterableApiStub {
78
78
  }
79
79
  class SearchResult extends _iterable.IdBasedIterableApiStub {
80
80
  constructor(api, response, options = {}) {
81
- super(api, '_searchGet', response.question_id, options);
81
+ super(api, '_searchGet', response.question_id, mergeCustomApiIterator(api, options));
82
82
  (0, _commons.defineValues)(this, response);
83
83
  this._response = response;
84
84
  }
@@ -90,4 +90,10 @@ class SearchResult extends _iterable.IdBasedIterableApiStub {
90
90
  // helpers //
91
91
  function isId(value) {
92
92
  return typeof value === 'string' && value.charAt(0) !== '{';
93
+ }
94
+ function mergeCustomApiIterator(api, options) {
95
+ const customIterator = api.helpers._root._customApiIterator;
96
+ return (0, _commons.trimObj)(Object.assign({
97
+ customIterator
98
+ }, options));
93
99
  }
@@ -6,35 +6,41 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _commons = require("@miso.ai/commons");
8
8
  var _constants = require("../constants.js");
9
+ const _excluded = ["method", "headers", "timeout"];
10
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
9
11
  class ApiHelpers {
10
12
  constructor(client, root) {
11
13
  this._client = client;
12
14
  this._root = root;
13
15
  this._bulk = new _commons.Bulk(this._runBulkFetch.bind(this), client._error.bind(client));
14
16
  }
15
- async fetch(url, payload, {
16
- method = 'POST',
17
- headers,
18
- timeout
19
- } = {}) {
17
+ async fetch(url, payload, _ref = {}) {
18
+ let {
19
+ method = 'POST',
20
+ headers,
21
+ timeout
22
+ } = _ref,
23
+ options = _objectWithoutPropertiesLoose(_ref, _excluded);
20
24
  const {
21
25
  apiKey,
22
26
  request = {}
23
27
  } = this._client.options;
24
28
  timeout = timeout || request.timeout;
25
29
 
26
- // TODO: external abort signal
27
30
  // TODO: organize arguments
28
31
  const body = method !== 'GET' && payload != undefined ? JSON.stringify(payload) : undefined;
32
+
33
+ // TODO: merge signal from options if any
29
34
  const signal = timeout ? AbortSignal.timeout(timeout) : undefined;
30
- const res = await (this._root._customFetch || window.fetch)(url, (0, _commons.trimObj)({
35
+ const res = await (this._root._customFetch || fetch)(url, (0, _commons.trimObj)(Object.assign({
31
36
  method,
32
37
  headers,
33
- body,
34
38
  cache: 'no-cache',
35
- mode: 'cors',
36
- signal
37
- }));
39
+ mode: 'cors'
40
+ }, options, {
41
+ signal,
42
+ body
43
+ })));
38
44
  const resBody = await res.json();
39
45
  if (res.status >= 400 || resBody.errors) {
40
46
  var err = new Error(resBody.message);
@@ -102,7 +108,10 @@ class ApiHelpers {
102
108
  if (method && method !== 'POST') {
103
109
  throw new Error(`Non-POST API is not supported in useBeacon mode: ${url}`);
104
110
  }
105
- const successful = this._root._customSendBeacon ? this._root._customSendBeacon(url, JSON.stringify(payload)) : window.navigator.sendBeacon(url, JSON.stringify(payload));
111
+ if (!this._root._customSendBeacon && !navigator.sendBeacon) {
112
+ throw new Error(`Send beacon is not supported in this environment.`);
113
+ }
114
+ const successful = this._root._customSendBeacon ? this._root._customSendBeacon(url, JSON.stringify(payload)) : navigator.sendBeacon(url, JSON.stringify(payload));
106
115
  if (!successful) {
107
116
  throw new Error(`Send beacon unsuccessful: ${url}`);
108
117
  }
@@ -113,7 +122,7 @@ class ApiHelpers {
113
122
  } = this._client.options;
114
123
  const apiName = paths.filter(s => s).join('/');
115
124
  const url = `${this.getApiEndpoint(apiName)}/${apiName}`;
116
- return `${url}?api_key=${window.encodeURIComponent(apiKey)}`;
125
+ return `${url}?api_key=${encodeURIComponent(apiKey)}`;
117
126
  }
118
127
  getApiEndpoint(apiName) {
119
128
  const {
@@ -27,8 +27,11 @@ class IterableApiStub {
27
27
  } = {}) => {
28
28
  // TODO: pass signal
29
29
  const response = await this.get();
30
- return [response, this._isFinished(response), this._revisionOf(response)];
30
+ return [response, isFinished(response), revisionOf(response)];
31
31
  };
32
+ if (typeof this._options.customIterator === 'function') {
33
+ return this._options.customIterator.call(this, fetch);
34
+ }
32
35
  let _this$_options = this._options,
33
36
  {
34
37
  pollingInterval: interval,
@@ -42,7 +45,7 @@ class IterableApiStub {
42
45
  const onResponse = (response, finished) => {
43
46
  if (finished) {
44
47
  stac.clear();
45
- } else if (this._isUpdated(prevResponse, response)) {
48
+ } else if (isUpdated(prevResponse, response)) {
46
49
  stac.touch();
47
50
  }
48
51
  prevResponse = response;
@@ -53,15 +56,6 @@ class IterableApiStub {
53
56
  onResponse
54
57
  }, options))[Symbol.asyncIterator]();
55
58
  }
56
- _isFinished(response) {
57
- return !!(response && response.finished);
58
- }
59
- _isUpdated(previousResponse, newResponse) {
60
- return !previousResponse || previousResponse.answer_stage !== newResponse.answer_stage || lengthOf(previousResponse.answer) !== lengthOf(newResponse.answer) || lengthOf(previousResponse.reasoning) !== lengthOf(newResponse.reasoning);
61
- }
62
- _revisionOf(response) {
63
- return response && response.revision;
64
- }
65
59
  }
66
60
  exports.IterableApiStub = IterableApiStub;
67
61
  class IdBasedIterableApiStub extends IterableApiStub {
@@ -76,6 +70,15 @@ class IdBasedIterableApiStub extends IterableApiStub {
76
70
  }
77
71
  }
78
72
  exports.IdBasedIterableApiStub = IdBasedIterableApiStub;
73
+ function isFinished(response) {
74
+ return !!(response && response.finished);
75
+ }
76
+ function isUpdated(previousResponse, newResponse) {
77
+ return !previousResponse || previousResponse.answer_stage !== newResponse.answer_stage || lengthOf(previousResponse.answer) !== lengthOf(newResponse.answer) || lengthOf(previousResponse.reasoning) !== lengthOf(newResponse.reasoning);
78
+ }
79
+ function revisionOf(response) {
80
+ return response && response.revision;
81
+ }
79
82
  function lengthOf(value = '') {
80
83
  return value.length;
81
84
  }
@@ -11,11 +11,13 @@ var _context = _interopRequireDefault(require("./context.js"));
11
11
  var helpers = _interopRequireWildcard(require("./utils.js"));
12
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
- const {
15
- currentScript
16
- } = document;
14
+ const currentScript = _commons.isInBrowser ? document.currentScript : undefined;
17
15
  class MisoClient extends _commons.Component {
18
16
  static attach() {
17
+ if (typeof window === 'undefined') {
18
+ console.warn(`MisoClient cannot be attached in non-browser environment.`);
19
+ return;
20
+ }
19
21
  (window.MisoClients || (window.MisoClients = [])).push(MisoClient);
20
22
  if (window.MisoClient) {
21
23
  if (window.MisoClient !== MisoClient) {
@@ -62,6 +64,10 @@ class MisoClient extends _commons.Component {
62
64
  delete options.apiHost;
63
65
  return (0, _commons.trimObj)(options);
64
66
  }
67
+
68
+ /**
69
+ * Used by shopify plugin.
70
+ */
65
71
  _readConfigFromScriptUrl() {
66
72
  const url = currentScript.src || currentScript.href; // might be <link> as well
67
73
  const params = new URL(new Request(url).url).searchParams;
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _commons = require("@miso.ai/commons");
8
- function _objectDestructuringEmpty(t) { if (null == t) throw new TypeError("Cannot destructure " + t); }
8
+ function _objectDestructuringEmpty(t) { if (null == t) throw new TypeError("Cannot destructure " + t); } // [browser only]
9
9
  const PLUGIN_ID = 'std:analytics';
10
10
  class AnalyticsPlugin extends _commons.Component {
11
11
  static get id() {
@@ -136,6 +136,12 @@ class Analytics {
136
136
  this._loadingAt = undefined;
137
137
  this._readyAt = undefined;
138
138
  this.config();
139
+ this._engagedSeconds = 0;
140
+ this._engagementInterval = setInterval(() => {
141
+ if (this._plugin._engagement.engaged && this._readyAt) {
142
+ this._engagedSeconds += 1;
143
+ }
144
+ }, 1000);
139
145
  }
140
146
  _injectWorkflow(workflow) {
141
147
  this._listenToLiftcycleEvents(workflow);
@@ -163,6 +169,7 @@ class Analytics {
163
169
  this._startAt = Date.now();
164
170
  this._loadingAt = undefined;
165
171
  this._readyAt = undefined;
172
+ this._engagedSeconds = 0;
166
173
  }
167
174
  _handleLoading({
168
175
  session
@@ -190,7 +197,8 @@ class Analytics {
190
197
  analytics_timestamp: state.timestamp,
191
198
  user_engagement_time: state.userEngagementTime,
192
199
  user_idle_time: state.userIdleTime,
193
- user_waiting_time: state.userWaitingTime
200
+ user_waiting_time: state.userWaitingTime,
201
+ user_engagement_time_v2: state.userEngagementTimeV2
194
202
  }
195
203
  }
196
204
  });
@@ -208,6 +216,7 @@ class Analytics {
208
216
  let userEngagementTime = 0,
209
217
  userIdleTime = 0,
210
218
  userWaitingTime = 0;
219
+ let userEngagementTimeV2 = this._engagedSeconds * 1000;
211
220
  if (loadingAt !== undefined) {
212
221
  userWaitingTime = currentTime - loadingAt;
213
222
  }
@@ -220,7 +229,8 @@ class Analytics {
220
229
  timestamp: currentTime,
221
230
  userWaitingTime,
222
231
  userEngagementTime,
223
- userIdleTime
232
+ userIdleTime,
233
+ userEngagementTimeV2
224
234
  });
225
235
  }
226
236
  _syncEngagementState() {
@@ -114,7 +114,7 @@ function getAutoAnonymousId() {
114
114
  // 1. Cache the value for the page
115
115
  // 2. Get and set the value from cookies/localStorage is possible
116
116
  // 3. In case cookies/localStorage are disabled, generate a new value for each page
117
- return _pageBasedAutoAnonymousId || (_pageBasedAutoAnonymousId = (0, _commons.getOrComputeFromStorage)('miso_anonymous_id', _commons.uuidv4));
117
+ return _pageBasedAutoAnonymousId || (_pageBasedAutoAnonymousId = _commons.isInBrowser ? (0, _commons.getOrComputeFromStorage)('miso_anonymous_id', _commons.uuidv4) : (0, _commons.uuidv4)());
118
118
  }
119
119
  class ClientContext {
120
120
  constructor(plugin) {
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _commons = require("@miso.ai/commons");
7
8
  const PLUGIN_ID = 'std:native-fetch';
8
9
  function isNativeFunction(fn) {
9
10
  return fn.toString().indexOf("[native code]") > -1;
@@ -11,6 +12,9 @@ function isNativeFunction(fn) {
11
12
  let _fetchSource;
12
13
  let _fetchError;
13
14
  const fetchFn = (() => {
15
+ if (!_commons.isInBrowser) {
16
+ return undefined;
17
+ }
14
18
  if (isNativeFunction(window.fetch)) {
15
19
  _fetchSource = 'original';
16
20
  return window.fetch;
@@ -32,6 +36,10 @@ class NativeFetchPlugin {
32
36
  return PLUGIN_ID;
33
37
  }
34
38
  install(MisoClient, context) {
39
+ if (!_commons.isInBrowser) {
40
+ MisoClient.debug(`<plugin:${PLUGIN_ID}>`, '[info]', `Not in browser, skipped`);
41
+ return;
42
+ }
35
43
  if (!fetchFn) {
36
44
  MisoClient.debug(`<plugin:${PLUGIN_ID}>`, '[error]', `Failed to obtain native fetch: ${_fetchError.message}`);
37
45
  return;
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _commons = require("@miso.ai/commons");
8
+ // [browser only]
9
+
8
10
  const ID = 'std:page-info';
9
11
  class PageInfoPlugin {
10
12
  static get id() {
@@ -16,7 +16,6 @@ class PluginRoot extends _commons.Registry {
16
16
  keyName: 'id'
17
17
  });
18
18
  this._root = root;
19
- this._currentScript = document.currentScript;
20
19
  this._events._replays.add('install').add('subtree');
21
20
  this._installed = {};
22
21
  this._installedResolutions = {};
@@ -116,6 +115,12 @@ class PluginRoot extends _commons.Registry {
116
115
  }
117
116
  this._root._customSendBeacon = sendBeacon;
118
117
  }
118
+ setCustomApiIterator(customApiIterator) {
119
+ if (typeof customApiIterator !== 'function') {
120
+ throw new Error(`Expect parameter to be a function: ${customApiIterator}`);
121
+ }
122
+ this._root._customApiIterator = customApiIterator;
123
+ }
119
124
  onHubUpdate(callback) {
120
125
  if (typeof callback !== 'function') {
121
126
  throw new Error(`Expect parameter to be a function: ${callback}`);
@@ -199,7 +204,7 @@ class PluginRoot extends _commons.Registry {
199
204
  }
200
205
  }
201
206
  async _tryRegisterRemotely(id) {
202
- if (id.startsWith('std:')) {
207
+ if (id.startsWith('std:') && _commons.isInBrowser) {
203
208
  try {
204
209
  await this._registerStdRemotely(id);
205
210
  return;
@@ -210,6 +215,11 @@ class PluginRoot extends _commons.Registry {
210
215
  // TODO: try as URL
211
216
  throw new Error(`Cannot register plugin remotely: ${id}`);
212
217
  }
218
+
219
+ /**
220
+ * [browser only]
221
+ * @param {string} id
222
+ */
213
223
  async _registerStdRemotely(id) {
214
224
  const {
215
225
  version
@@ -221,7 +231,7 @@ class PluginRoot extends _commons.Registry {
221
231
  // skip if already exists
222
232
  const script = document.createElement('script');
223
233
  script.async = true;
224
- script.src = (0, _urls.getPluginScriptUrl)(id, version, this._currentScript);
234
+ script.src = (0, _urls.getPluginScriptUrl)(id, version);
225
235
  script.setAttribute('data-request', request);
226
236
  document.head.appendChild(script);
227
237
  // TODO: hook up error event
@@ -239,7 +249,7 @@ class PluginRoot extends _commons.Registry {
239
249
  exports.default = PluginRoot;
240
250
  class PluginContext {
241
251
  constructor(root, classes) {
242
- (0, _commons.delegateGetters)(this, root, ['getPluginClass', 'addSubtree', 'addPayloadPass', 'addHeadersPass', 'addUrlPass', 'setCustomFetch', 'setCustomSendBeacon', 'onHubUpdate', 'onHubEmit', 'whenInstalled', 'whenRegistered']);
252
+ (0, _commons.delegateGetters)(this, root, ['getPluginClass', 'addSubtree', 'addPayloadPass', 'addHeadersPass', 'addUrlPass', 'setCustomFetch', 'setCustomSendBeacon', 'setCustomApiIterator', 'onHubUpdate', 'onHubEmit', 'whenInstalled', 'whenRegistered']);
243
253
  (0, _commons.defineValues)(this, {
244
254
  classes
245
255
  });
@@ -4,10 +4,15 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.getPluginScriptUrl = getPluginScriptUrl;
7
- function getPluginScriptUrl(id, version, currentScript) {
7
+ var _commons = require("@miso.ai/commons");
8
+ const currentScript = _commons.isInBrowser ? document.currentScript : undefined;
9
+ function getPluginScriptUrl(id, version) {
8
10
  // TODO: look up by manifest
9
11
  const pkgName = id.slice(4);
10
12
  if (version === 'dev') {
13
+ if (!currentScript) {
14
+ throw new Error(`Cannot get plugin script URL for ${id}: no currentScript.`);
15
+ }
11
16
  const src = currentScript.src;
12
17
  const searchIndex = src.indexOf('?');
13
18
  const k = searchIndex < 0 ? src.length : searchIndex;
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _default = exports.default = '1.13.0-beta.8';
7
+ var _default = exports.default = '1.13.1-beta.0';
package/package.json CHANGED
@@ -30,7 +30,7 @@
30
30
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
31
31
  },
32
32
  "dependencies": {
33
- "@miso.ai/commons": "1.13.0-beta.8"
33
+ "@miso.ai/commons": "1.13.1-beta.0"
34
34
  },
35
- "version": "1.13.0-beta.8"
35
+ "version": "1.13.1-beta.0"
36
36
  }
package/src/api/ask.js CHANGED
@@ -1,4 +1,4 @@
1
- import { API, defineValues } from '@miso.ai/commons';
1
+ import { API, defineValues, trimObj } from '@miso.ai/commons';
2
2
  import ApiBase from './base.js';
3
3
  import { IdBasedIterableApiStub } from './iterable.js';
4
4
 
@@ -65,7 +65,7 @@ export default class Ask extends ApiBase {
65
65
  class Answer extends IdBasedIterableApiStub {
66
66
 
67
67
  constructor(api, response, options = {}) {
68
- super(api, '_questionGet', response.question_id, options);
68
+ super(api, '_questionGet', response.question_id, mergeCustomApiIterator(api, options));
69
69
  this._response = response;
70
70
  }
71
71
 
@@ -78,7 +78,7 @@ class Answer extends IdBasedIterableApiStub {
78
78
  class SearchResult extends IdBasedIterableApiStub {
79
79
 
80
80
  constructor(api, response, options = {}) {
81
- super(api, '_searchGet', response.question_id, options);
81
+ super(api, '_searchGet', response.question_id, mergeCustomApiIterator(api, options));
82
82
  defineValues(this, response);
83
83
  this._response = response;
84
84
  }
@@ -93,3 +93,11 @@ class SearchResult extends IdBasedIterableApiStub {
93
93
  function isId(value) {
94
94
  return typeof value === 'string' && value.charAt(0) !== '{';
95
95
  }
96
+
97
+ function mergeCustomApiIterator(api, options) {
98
+ const customIterator = api.helpers._root._customApiIterator;
99
+ return trimObj({
100
+ customIterator,
101
+ ...options,
102
+ });
103
+ }
@@ -13,23 +13,25 @@ export default class ApiHelpers {
13
13
  method = 'POST',
14
14
  headers,
15
15
  timeout,
16
+ ...options
16
17
  } = {}) {
17
18
  const { apiKey, request = {} } = this._client.options;
18
19
  timeout = timeout || request.timeout;
19
20
 
20
- // TODO: external abort signal
21
21
  // TODO: organize arguments
22
22
  const body = method !== 'GET' && payload != undefined ? JSON.stringify(payload) : undefined;
23
23
 
24
+ // TODO: merge signal from options if any
24
25
  const signal = timeout ? AbortSignal.timeout(timeout) : undefined;
25
26
 
26
- const res = await (this._root._customFetch || window.fetch)(url, trimObj({
27
+ const res = await (this._root._customFetch || fetch)(url, trimObj({
27
28
  method,
28
29
  headers,
29
- body,
30
30
  cache: 'no-cache',
31
31
  mode: 'cors',
32
+ ...options,
32
33
  signal,
34
+ body,
33
35
  }));
34
36
 
35
37
  const resBody = await res.json();
@@ -78,9 +80,12 @@ export default class ApiHelpers {
78
80
  if (method && method !== 'POST') {
79
81
  throw new Error(`Non-POST API is not supported in useBeacon mode: ${url}`);
80
82
  }
83
+ if (!this._root._customSendBeacon && !navigator.sendBeacon) {
84
+ throw new Error(`Send beacon is not supported in this environment.`);
85
+ }
81
86
  const successful = this._root._customSendBeacon ?
82
87
  this._root._customSendBeacon(url, JSON.stringify(payload)) :
83
- window.navigator.sendBeacon(url, JSON.stringify(payload));
88
+ navigator.sendBeacon(url, JSON.stringify(payload));
84
89
  if (!successful) {
85
90
  throw new Error(`Send beacon unsuccessful: ${url}`);
86
91
  }
@@ -90,7 +95,7 @@ export default class ApiHelpers {
90
95
  const { apiKey } = this._client.options;
91
96
  const apiName = paths.filter(s => s).join('/');
92
97
  const url = `${this.getApiEndpoint(apiName)}/${apiName}`;
93
- return `${url}?api_key=${window.encodeURIComponent(apiKey)}`;
98
+ return `${url}?api_key=${encodeURIComponent(apiKey)}`;
94
99
  }
95
100
 
96
101
  getApiEndpoint(apiName) {
@@ -23,8 +23,13 @@ export class IterableApiStub {
23
23
  const fetch = async ({ signal } = {}) => {
24
24
  // TODO: pass signal
25
25
  const response = await this.get();
26
- return [response, this._isFinished(response), this._revisionOf(response)];
26
+ return [response, isFinished(response), revisionOf(response)];
27
27
  };
28
+
29
+ if (typeof this._options.customIterator === 'function') {
30
+ return this._options.customIterator.call(this, fetch);
31
+ }
32
+
28
33
  let { pollingInterval: interval, signal, stallTimeout = 120000, ...options } = this._options;
29
34
  const stac = new StallTimeoutAbortController(stallTimeout);
30
35
  signal = signals.any(this._ac.signal, stac.signal, signal);
@@ -33,7 +38,7 @@ export class IterableApiStub {
33
38
  const onResponse = (response, finished) => {
34
39
  if (finished) {
35
40
  stac.clear();
36
- } else if (this._isUpdated(prevResponse, response)) {
41
+ } else if (isUpdated(prevResponse, response)) {
37
42
  stac.touch();
38
43
  }
39
44
  prevResponse = response;
@@ -41,21 +46,6 @@ export class IterableApiStub {
41
46
  return polling(fetch, { interval, signal, onResponse, ...options })[Symbol.asyncIterator]();
42
47
  }
43
48
 
44
- _isFinished(response) {
45
- return !!(response && response.finished);
46
- }
47
-
48
- _isUpdated(previousResponse, newResponse) {
49
- return !previousResponse ||
50
- previousResponse.answer_stage !== newResponse.answer_stage ||
51
- lengthOf(previousResponse.answer) !== lengthOf(newResponse.answer) ||
52
- lengthOf(previousResponse.reasoning) !== lengthOf(newResponse.reasoning);
53
- }
54
-
55
- _revisionOf(response) {
56
- return response && response.revision;
57
- }
58
-
59
49
  }
60
50
 
61
51
  export class IdBasedIterableApiStub extends IterableApiStub {
@@ -73,6 +63,21 @@ export class IdBasedIterableApiStub extends IterableApiStub {
73
63
 
74
64
  }
75
65
 
66
+ function isFinished(response) {
67
+ return !!(response && response.finished);
68
+ }
69
+
70
+ function isUpdated(previousResponse, newResponse) {
71
+ return !previousResponse ||
72
+ previousResponse.answer_stage !== newResponse.answer_stage ||
73
+ lengthOf(previousResponse.answer) !== lengthOf(newResponse.answer) ||
74
+ lengthOf(previousResponse.reasoning) !== lengthOf(newResponse.reasoning);
75
+ }
76
+
77
+ function revisionOf(response) {
78
+ return response && response.revision;
79
+ }
80
+
76
81
  function lengthOf(value = '') {
77
82
  return value.length;
78
83
  }
package/src/client.js CHANGED
@@ -1,14 +1,18 @@
1
- import { trimObj, Component, uuidv4, defineValues } from '@miso.ai/commons';
1
+ import { trimObj, Component, uuidv4, defineValues, isInBrowser } from '@miso.ai/commons';
2
2
  import { root, register, init } from './root.js';
3
3
  import { Api } from './api/index.js';
4
4
  import Context from './context.js';
5
5
  import * as helpers from './utils.js';
6
6
 
7
- const { currentScript } = document;
7
+ const currentScript = isInBrowser ? document.currentScript : undefined;
8
8
 
9
9
  class MisoClient extends Component {
10
10
 
11
11
  static attach() {
12
+ if (typeof window === 'undefined') {
13
+ console.warn(`MisoClient cannot be attached in non-browser environment.`);
14
+ return;
15
+ }
12
16
  (window.MisoClients || (window.MisoClients = [])).push(MisoClient);
13
17
  if (window.MisoClient) {
14
18
  if (window.MisoClient !== MisoClient) {
@@ -65,6 +69,9 @@ class MisoClient extends Component {
65
69
  return trimObj(options);
66
70
  }
67
71
 
72
+ /**
73
+ * Used by shopify plugin.
74
+ */
68
75
  _readConfigFromScriptUrl() {
69
76
  const url = currentScript.src || currentScript.href; // might be <link> as well
70
77
  const params = new URL(new Request(url).url).searchParams;
@@ -1,3 +1,5 @@
1
+ // [browser only]
2
+
1
3
  import { Component, UserEngagementObserver, UserActivityObserver, Stopwatch, trimObj, mergeInteractions } from '@miso.ai/commons';
2
4
 
3
5
  const PLUGIN_ID = 'std:analytics';
@@ -150,6 +152,13 @@ class Analytics {
150
152
  this._readyAt = undefined;
151
153
 
152
154
  this.config();
155
+
156
+ this._engagedSeconds = 0;
157
+ this._engagementInterval = setInterval(() => {
158
+ if (this._plugin._engagement.engaged && this._readyAt) {
159
+ this._engagedSeconds += 1;
160
+ }
161
+ }, 1000);
153
162
  }
154
163
 
155
164
  _injectWorkflow(workflow) {
@@ -183,6 +192,8 @@ class Analytics {
183
192
  this._startAt = Date.now();
184
193
  this._loadingAt = undefined;
185
194
  this._readyAt = undefined;
195
+
196
+ this._engagedSeconds = 0;
186
197
  }
187
198
 
188
199
  _handleLoading({ session } = {}) {
@@ -209,6 +220,7 @@ class Analytics {
209
220
  user_engagement_time: state.userEngagementTime,
210
221
  user_idle_time: state.userIdleTime,
211
222
  user_waiting_time: state.userWaitingTime,
223
+ user_engagement_time_v2: state.userEngagementTimeV2,
212
224
  },
213
225
  },
214
226
  });
@@ -229,6 +241,7 @@ class Analytics {
229
241
  const loadingAt = this._loadingAt;
230
242
  const readyAt = this._readyAt;
231
243
  let userEngagementTime = 0, userIdleTime = 0, userWaitingTime = 0;
244
+ let userEngagementTimeV2 = this._engagedSeconds * 1000;
232
245
 
233
246
  if (loadingAt !== undefined) {
234
247
  userWaitingTime = currentTime - loadingAt;
@@ -244,6 +257,7 @@ class Analytics {
244
257
  userWaitingTime,
245
258
  userEngagementTime,
246
259
  userIdleTime,
260
+ userEngagementTimeV2,
247
261
  });
248
262
  }
249
263
 
@@ -1,4 +1,4 @@
1
- import { API, uuidv4, Component, delegateProperties, getOrComputeFromStorage, mergeInteractions, mergeApiPayloads } from '@miso.ai/commons';
1
+ import { API, uuidv4, Component, delegateProperties, getOrComputeFromStorage, mergeInteractions, mergeApiPayloads, isInBrowser } from '@miso.ai/commons';
2
2
 
3
3
  const { GROUP, NAME } = API;
4
4
 
@@ -73,7 +73,7 @@ function getAutoAnonymousId() {
73
73
  // 1. Cache the value for the page
74
74
  // 2. Get and set the value from cookies/localStorage is possible
75
75
  // 3. In case cookies/localStorage are disabled, generate a new value for each page
76
- return _pageBasedAutoAnonymousId || (_pageBasedAutoAnonymousId = getOrComputeFromStorage('miso_anonymous_id', uuidv4));
76
+ return _pageBasedAutoAnonymousId || (_pageBasedAutoAnonymousId = isInBrowser ? getOrComputeFromStorage('miso_anonymous_id', uuidv4) : uuidv4());
77
77
  }
78
78
 
79
79
  class ClientContext {
@@ -1,3 +1,5 @@
1
+ import { isInBrowser } from '@miso.ai/commons';
2
+
1
3
  const PLUGIN_ID = 'std:native-fetch';
2
4
 
3
5
  function isNativeFunction(fn) {
@@ -8,6 +10,9 @@ let _fetchSource;
8
10
  let _fetchError;
9
11
 
10
12
  const fetchFn = (() => {
13
+ if (!isInBrowser) {
14
+ return undefined;
15
+ }
11
16
  if (isNativeFunction(window.fetch)) {
12
17
  _fetchSource = 'original';
13
18
  return window.fetch;
@@ -32,6 +37,10 @@ export default class NativeFetchPlugin {
32
37
  }
33
38
 
34
39
  install(MisoClient, context) {
40
+ if (!isInBrowser) {
41
+ MisoClient.debug(`<plugin:${PLUGIN_ID}>`, '[info]', `Not in browser, skipped`);
42
+ return;
43
+ }
35
44
  if (!fetchFn) {
36
45
  MisoClient.debug(`<plugin:${PLUGIN_ID}>`, '[error]', `Failed to obtain native fetch: ${_fetchError.message}`);
37
46
  return;
@@ -1,3 +1,5 @@
1
+ // [browser only]
2
+
1
3
  import { trimObj } from '@miso.ai/commons';
2
4
 
3
5
  const ID = 'std:page-info';
@@ -1,4 +1,4 @@
1
- import { delegateGetters, defineValues, Registry, Resolution, Resources } from '@miso.ai/commons';
1
+ import { isInBrowser, delegateGetters, defineValues, Registry, Resolution, Resources } from '@miso.ai/commons';
2
2
  import classes from '../classes.js';
3
3
  import AnonymousPlugin from './anonymous.js';
4
4
  import { getPluginScriptUrl } from './urls.js';
@@ -11,7 +11,6 @@ export default class PluginRoot extends Registry {
11
11
  keyName: 'id',
12
12
  });
13
13
  this._root = root;
14
- this._currentScript = document.currentScript;
15
14
  this._events._replays.add('install').add('subtree');
16
15
  this._installed = {};
17
16
  this._installedResolutions = {};
@@ -122,6 +121,13 @@ export default class PluginRoot extends Registry {
122
121
  this._root._customSendBeacon = sendBeacon;
123
122
  }
124
123
 
124
+ setCustomApiIterator(customApiIterator) {
125
+ if (typeof customApiIterator !== 'function') {
126
+ throw new Error(`Expect parameter to be a function: ${customApiIterator}`);
127
+ }
128
+ this._root._customApiIterator = customApiIterator;
129
+ }
130
+
125
131
  onHubUpdate(callback) {
126
132
  if (typeof callback !== 'function') {
127
133
  throw new Error(`Expect parameter to be a function: ${callback}`);
@@ -210,7 +216,7 @@ export default class PluginRoot extends Registry {
210
216
  }
211
217
 
212
218
  async _tryRegisterRemotely(id) {
213
- if (id.startsWith('std:')) {
219
+ if (id.startsWith('std:') && isInBrowser) {
214
220
  try {
215
221
  await this._registerStdRemotely(id);
216
222
  return;
@@ -222,6 +228,10 @@ export default class PluginRoot extends Registry {
222
228
  throw new Error(`Cannot register plugin remotely: ${id}`);
223
229
  }
224
230
 
231
+ /**
232
+ * [browser only]
233
+ * @param {string} id
234
+ */
225
235
  async _registerStdRemotely(id) {
226
236
  const { version } = this._root;
227
237
  const request = version === 'dev' ? `plugin:${id}@dev:${this.MisoClient.uuid}` : `plugin:${id}@${version}`;
@@ -231,7 +241,7 @@ export default class PluginRoot extends Registry {
231
241
  // skip if already exists
232
242
  const script = document.createElement('script');
233
243
  script.async = true;
234
- script.src = getPluginScriptUrl(id, version, this._currentScript);
244
+ script.src = getPluginScriptUrl(id, version);
235
245
  script.setAttribute('data-request', request);
236
246
  document.head.appendChild(script);
237
247
  // TODO: hook up error event
@@ -250,7 +260,7 @@ export default class PluginRoot extends Registry {
250
260
  class PluginContext {
251
261
 
252
262
  constructor(root, classes) {
253
- delegateGetters(this, root, ['getPluginClass', 'addSubtree', 'addPayloadPass', 'addHeadersPass', 'addUrlPass', 'setCustomFetch', 'setCustomSendBeacon', 'onHubUpdate', 'onHubEmit', 'whenInstalled', 'whenRegistered']);
263
+ delegateGetters(this, root, ['getPluginClass', 'addSubtree', 'addPayloadPass', 'addHeadersPass', 'addUrlPass', 'setCustomFetch', 'setCustomSendBeacon', 'setCustomApiIterator', 'onHubUpdate', 'onHubEmit', 'whenInstalled', 'whenRegistered']);
254
264
  defineValues(this, { classes });
255
265
  }
256
266
 
@@ -1,8 +1,15 @@
1
- export function getPluginScriptUrl(id, version, currentScript) {
1
+ import { isInBrowser } from '@miso.ai/commons';
2
+
3
+ const currentScript = isInBrowser ? document.currentScript : undefined;
4
+
5
+ export function getPluginScriptUrl(id, version) {
2
6
  // TODO: look up by manifest
3
7
  const pkgName = id.slice(4);
4
8
 
5
9
  if (version === 'dev') {
10
+ if (!currentScript) {
11
+ throw new Error(`Cannot get plugin script URL for ${id}: no currentScript.`);
12
+ }
6
13
  const src = currentScript.src;
7
14
  const searchIndex = src.indexOf('?');
8
15
  const k = searchIndex < 0 ? src.length : searchIndex;
package/src/version.js CHANGED
@@ -1 +1 @@
1
- export default '1.13.0-beta.8';
1
+ export default '1.13.1-beta.0';