@miso.ai/client-sdk-core 1.8.0-beta.16 → 1.8.0-beta.18

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.
Files changed (47) hide show
  1. package/dist/cjs/api/api.js +25 -0
  2. package/dist/cjs/api/ask.js +1 -1
  3. package/dist/cjs/api/helpers.js +1 -1
  4. package/dist/cjs/api/index.js +49 -19
  5. package/dist/cjs/api/interactions.js +1 -1
  6. package/dist/cjs/api/recommendation.js +1 -1
  7. package/dist/cjs/api/search.js +1 -1
  8. package/dist/cjs/classes.js +5 -15
  9. package/dist/cjs/client.js +2 -2
  10. package/dist/cjs/index.js +31 -5
  11. package/dist/cjs/plugin/index.js +3 -3
  12. package/dist/cjs/plugin/plugin-root.js +3 -3
  13. package/dist/cjs/root.js +2 -2
  14. package/dist/cjs/version.js +1 -1
  15. package/package.json +9 -9
  16. package/{dist/es/api/index.js → src/api/api.js} +9 -6
  17. package/src/api/ask.js +75 -0
  18. package/src/api/base.js +47 -0
  19. package/{dist/es → src}/api/helpers.js +47 -91
  20. package/src/api/index.js +7 -0
  21. package/src/api/interactions.js +54 -0
  22. package/{dist/es → src}/api/recommendation.js +11 -2
  23. package/{dist/es → src}/api/search.js +8 -2
  24. package/src/classes.js +7 -0
  25. package/{dist/es → src}/client.js +25 -10
  26. package/{dist/es → src}/cmd.js +7 -8
  27. package/{dist/es → src}/constants.js +2 -2
  28. package/{dist/es → src}/context.js +4 -1
  29. package/src/index.js +3 -0
  30. package/{dist/es → src}/plugin/anonymous.js +5 -4
  31. package/{dist/es → src}/plugin/auto-events.js +24 -25
  32. package/src/plugin/index.js +3 -0
  33. package/{dist/es → src}/plugin/page-info.js +28 -24
  34. package/{dist/es → src}/plugin/plugin-root.js +37 -22
  35. package/{dist/es → src}/plugin/urls.js +2 -1
  36. package/{dist/es → src}/plugin/user.js +32 -37
  37. package/{dist/es → src}/root.js +10 -4
  38. package/src/utils.js +1 -0
  39. package/src/version.js +1 -0
  40. package/dist/es/api/ask.js +0 -85
  41. package/dist/es/api/base.js +0 -96
  42. package/dist/es/api/interactions.js +0 -82
  43. package/dist/es/classes.js +0 -20
  44. package/dist/es/index.js +0 -2
  45. package/dist/es/plugin/index.js +0 -3
  46. package/dist/es/utils.js +0 -1
  47. package/dist/es/version.js +0 -1
@@ -1,41 +1,42 @@
1
1
  import { Bulk, trimObj } from '@miso.ai/commons';
2
- import { API } from '../constants';
2
+ import { API } from '../constants.js';
3
+
3
4
  export default class ApiHelpers {
5
+
4
6
  constructor(client, root) {
5
7
  this._client = client;
6
8
  this._root = root;
7
9
  this._bulk = new Bulk(this._runBulkFetch.bind(this), client._error.bind(client));
8
10
  }
9
- async fetch(url, payload) {
10
- let {
11
- method = 'POST',
12
- headers,
13
- timeout,
14
- sendApiKeyByHeader
15
- } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
16
- const {
17
- apiKey,
18
- request = {}
19
- } = this._client.options;
11
+
12
+ async fetch(url, payload, {
13
+ method = 'POST',
14
+ headers,
15
+ timeout,
16
+ sendApiKeyByHeader,
17
+ } = {}) {
18
+ const { apiKey, request = {} } = this._client.options;
20
19
  timeout = timeout || request.timeout;
21
20
  sendApiKeyByHeader = sendApiKeyByHeader || request.sendApiKeyByHeader;
21
+
22
22
  if (sendApiKeyByHeader) {
23
- headers = Object.assign({}, headers, {
24
- 'X-API-KEY': apiKey
25
- });
23
+ headers = { ...headers, 'X-API-KEY': apiKey };
26
24
  }
27
25
  // TODO: external abort signal
28
26
  // TODO: organize arguments
29
27
  const body = method !== 'GET' && payload != undefined ? JSON.stringify(payload) : undefined;
28
+
30
29
  const signal = timeout ? AbortSignal.timeout(timeout) : undefined;
30
+
31
31
  const res = await window.fetch(url, trimObj({
32
32
  method,
33
33
  headers,
34
34
  body,
35
35
  cache: 'no-cache',
36
36
  mode: 'cors',
37
- signal
37
+ signal,
38
38
  }));
39
+
39
40
  const resBody = await res.json();
40
41
  if (res.status >= 400 || resBody.errors) {
41
42
  var err = new Error(resBody.message);
@@ -45,66 +46,40 @@ export default class ApiHelpers {
45
46
  }
46
47
  return resBody;
47
48
  }
49
+
48
50
  get bulkInfo() {
49
51
  return this._bulk.info;
50
52
  }
51
- async fetchForBulk(apiGroup, apiName, payload) {
52
- let {
53
- method
54
- } = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
53
+
54
+ async fetchForBulk(apiGroup, apiName, payload, { method } = {}) {
55
55
  if (method && method !== 'POST') {
56
56
  throw new Error(`Non-POST API is not supported in bulk mode: ${url}`);
57
57
  }
58
- return this._bulk.run({
59
- apiGroup,
60
- apiName,
61
- payload
62
- });
58
+ return this._bulk.run({ apiGroup, apiName, payload });
63
59
  }
60
+
64
61
  async _runBulkFetch(requests) {
65
62
  // TODO: request options?
66
63
  const url = this.url(['bulk']);
67
64
  const payload = {
68
- requests: requests.map(_ref => {
69
- let {
70
- action: {
71
- apiGroup,
72
- apiName,
73
- payload
74
- }
75
- } = _ref;
76
- return {
77
- api_name: `${apiGroup}/${apiName}`,
78
- body: payload
79
- };
80
- })
65
+ requests: requests.map(({ action: { apiGroup, apiName, payload } }) => ({
66
+ api_name: `${apiGroup}/${apiName}`,
67
+ body: payload,
68
+ })),
81
69
  };
82
- const {
83
- data: responses
84
- } = await this.fetch(url, payload);
70
+ const { data: responses } = await this.fetch(url, payload);
85
71
  for (let i = 0, len = requests.length; i < len; i++) {
86
- const {
87
- resolution
88
- } = requests[i];
89
- const {
90
- error,
91
- status_code,
92
- body
93
- } = responses[i];
72
+ const { resolution } = requests[i];
73
+ const { error, status_code, body } = responses[i];
94
74
  if (status_code >= 400 || error) {
95
- resolution.reject({
96
- status_code,
97
- body
98
- });
75
+ resolution.reject({ status_code, body });
99
76
  } else {
100
77
  resolution.resolve(body);
101
78
  }
102
79
  }
103
80
  }
104
- sendBeacon(url, payload) {
105
- let {
106
- method
107
- } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
81
+
82
+ sendBeacon(url, payload, { method } = {}) {
108
83
  if (method && method !== 'POST') {
109
84
  throw new Error(`Non-POST API is not supported in useBeacon mode: ${url}`);
110
85
  }
@@ -112,64 +87,45 @@ export default class ApiHelpers {
112
87
  throw new Error(`Send beacon unsuccessful: ${url}`);
113
88
  }
114
89
  }
115
- url(paths) {
116
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
117
- const {
118
- apiKey,
119
- request = {}
120
- } = this._client.options;
90
+
91
+ url(paths, options = {}) {
92
+ const { apiKey, request = {} } = this._client.options;
121
93
  const sendApiKeyByHeader = options.sendApiKeyByHeader || request.sendApiKeyByHeader;
122
94
  const apiName = paths.filter(s => s).join('/');
123
95
  const url = `${this.getApiEndpoint(apiName)}/${apiName}`;
124
96
  return sendApiKeyByHeader ? url : `${url}?api_key=${window.encodeURIComponent(apiKey)}`;
125
97
  }
98
+
126
99
  getApiEndpoint(apiName) {
127
100
  const {
128
101
  dataEndpoint = API.DATA_ENDPOINT,
129
- eventEndpoint = API.EVENT_ENDPOINT
102
+ eventEndpoint = API.EVENT_ENDPOINT,
130
103
  } = this._client.options;
131
104
  return apiName === 'interactions' ? eventEndpoint : dataEndpoint;
132
105
  }
133
- applyUrlPasses(component, _ref2) {
134
- let {
135
- apiGroup,
136
- apiName,
137
- url
138
- } = _ref2;
106
+
107
+ applyUrlPasses(component, { apiGroup, apiName, url }) {
139
108
  const client = this._client;
140
109
  for (const pass of this._root._urlPasses) {
141
110
  try {
142
- url = pass({
143
- client,
144
- apiGroup,
145
- apiName,
146
- url
147
- }) || url;
148
- } catch (e) {
111
+ url = pass({ client, apiGroup, apiName, url }) || url;
112
+ } catch(e) {
149
113
  component.error(e);
150
114
  }
151
115
  }
152
116
  return url;
153
117
  }
154
- applyPayloadPasses(component, _ref3) {
155
- let {
156
- apiGroup,
157
- apiName,
158
- payload
159
- } = _ref3;
118
+
119
+ applyPayloadPasses(component, { apiGroup, apiName, payload }) {
160
120
  const client = this._client;
161
121
  for (const pass of this._root._payloadPasses) {
162
122
  try {
163
- payload = pass({
164
- client,
165
- apiGroup,
166
- apiName,
167
- payload
168
- }) || payload;
169
- } catch (e) {
123
+ payload = pass({ client, apiGroup, apiName, payload }) || payload;
124
+ } catch(e) {
170
125
  component.error(e);
171
126
  }
172
127
  }
173
128
  return payload;
174
129
  }
175
- }
130
+
131
+ }
@@ -0,0 +1,7 @@
1
+ export { default as Api } from './api.js';
2
+ export { default as ApiBase } from './base.js';
3
+ export { default as ApiHelpers } from './helpers.js';
4
+ export { default as Interactions } from './interactions.js';
5
+ export { default as Ask } from './ask.js';
6
+ export { default as Search } from './search.js';
7
+ export { default as Recommendation } from './recommendation.js';
@@ -0,0 +1,54 @@
1
+ import ApiBase from './base.js';
2
+
3
+ export default class Interactions extends ApiBase {
4
+
5
+ constructor(api) {
6
+ super(api, 'interactions');
7
+ }
8
+
9
+ async upload(payload, options) {
10
+ try {
11
+ return await this._run('upload', payload, options);
12
+ } catch (e) {
13
+ if (e.status === 400 && e.message && e.message.toLowerCase().indexOf('playground') > -1) {
14
+ this._warn(`Ignore interactions uploaded to playground app.`);
15
+ } else {
16
+ throw e;
17
+ }
18
+ }
19
+ }
20
+
21
+ _url({ apiGroup, apiName, options }) {
22
+ const url = this.helpers.url([this._apiPath], options);
23
+ return this.helpers.applyUrlPasses(this, { apiGroup, apiName, url, options });
24
+ }
25
+
26
+ _preprocess({ apiGroup, apiName, payload }) {
27
+ if (apiName === 'upload') {
28
+ payload = this._normalizeUploadPayload(payload);
29
+ }
30
+ return super._preprocess({ apiGroup, apiName, payload });
31
+ }
32
+
33
+ _normalizeUploadPayload(payload) {
34
+ // TODO: accept string as a shortcut of { type: payload } as well
35
+ if (typeof payload !== 'object') {
36
+ throw new Error(`Interactions payload has to be either an object or an array of objects: ${payload}`);
37
+ }
38
+ if (!Array.isArray(payload)) {
39
+ payload = [payload];
40
+ }
41
+ return { data: payload };
42
+ }
43
+
44
+ async _send({ options: { useBeacon = true, ...options } = {}, ...args }) {
45
+ if (useBeacon) {
46
+ const { url, payload } = args;
47
+ this.helpers.sendBeacon(url, payload, options);
48
+ return {};
49
+ } else {
50
+ return super._send({ ...args, options });
51
+ }
52
+ }
53
+
54
+ }
@@ -1,24 +1,33 @@
1
- import ApiBase from './base';
1
+ import ApiBase from './base.js';
2
+
2
3
  export default class Recommendation extends ApiBase {
4
+
3
5
  constructor(api) {
4
6
  super(api, 'recommendation');
5
7
  }
8
+
6
9
  async userToProducts(payload, options) {
7
10
  return this._run('user_to_products', payload, options);
8
11
  }
12
+
9
13
  async userToCategories(payload, options) {
10
14
  return this._run('user_to_categories', payload, options);
11
15
  }
16
+
12
17
  async userToAttributes(payload, options) {
13
18
  return this._run('user_to_attributes', payload, options);
14
19
  }
20
+
15
21
  async userToTrending(payload, options) {
16
22
  return this._run('user_to_trending', payload, options);
17
23
  }
24
+
18
25
  async userToHistory(payload, options) {
19
26
  return this._run('user_to_history', payload, options);
20
27
  }
28
+
21
29
  async productToProducts(payload, options) {
22
30
  return this._run('product_to_products', payload, options);
23
31
  }
24
- }
32
+
33
+ }
@@ -1,15 +1,21 @@
1
- import ApiBase from './base';
1
+ import ApiBase from './base.js';
2
+
2
3
  export default class Search extends ApiBase {
4
+
3
5
  constructor(api) {
4
6
  super(api, 'search');
5
7
  }
8
+
6
9
  async search(payload, options) {
7
10
  return this._run('search', payload, options);
8
11
  }
12
+
9
13
  async autocomplete(payload, options) {
10
14
  return this._run('autocomplete', payload, options);
11
15
  }
16
+
12
17
  async multipleGet(payload, options) {
13
18
  return this._run('mget', payload, options);
14
19
  }
15
- }
20
+
21
+ }
package/src/classes.js ADDED
@@ -0,0 +1,7 @@
1
+ import * as api from './api/index.js';
2
+ import Context from './context.js';
3
+
4
+ export default {
5
+ api,
6
+ context: { Context },
7
+ }
@@ -1,9 +1,11 @@
1
1
  import { trimObj, Component, uuidv4, defineValues } from '@miso.ai/commons';
2
2
  import { root, register, init } from './root';
3
- import Api from './api';
3
+ import { Api } from './api/index.js';
4
4
  import Context from './context';
5
5
  import * as helpers from './utils';
6
+
6
7
  class MisoClient extends Component {
8
+
7
9
  static attach() {
8
10
  (window.MisoClients || (window.MisoClients = [])).push(MisoClient);
9
11
  if (window.MisoClient) {
@@ -15,48 +17,61 @@ class MisoClient extends Component {
15
17
  }
16
18
  window.MisoClient = MisoClient;
17
19
  }
20
+
18
21
  constructor(options) {
19
22
  super('client', root);
20
23
  this._config(options);
24
+
21
25
  this.context = new Context(this);
22
26
  this.api = new Api(this, root);
27
+
23
28
  register(this);
24
29
  }
30
+
25
31
  get version() {
26
32
  return MisoClient.version;
27
33
  }
34
+
28
35
  _config(options) {
29
36
  this.options = Object.freeze(this._normalizeOptions(options));
30
37
  }
31
- _normalizeOptions() {
32
- let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
38
+
39
+ _normalizeOptions(options = {}) {
33
40
  if (typeof options === 'string') {
34
- options = {
35
- apiKey: options
36
- };
41
+ options = { apiKey: options };
37
42
  }
38
43
  if (options.readConfigFromScriptUrl) {
39
- options = Object.assign({}, this._readConfigFromScriptUrl(), options);
44
+ options = {
45
+ ...this._readConfigFromScriptUrl(),
46
+ ...options,
47
+ };
40
48
  }
41
49
  if (!options.apiKey) {
42
50
  throw new Error('Require API key to initialize miso client.');
43
51
  }
52
+
44
53
  options.dataEndpoint = options.dataEndpoint || options.apiHost;
45
54
  options.eventEndpoint = options.eventEndpoint || options.apiHost;
46
55
  delete options.apiHost;
56
+
47
57
  return trimObj(options);
48
58
  }
59
+
49
60
  _readConfigFromScriptUrl() {
50
61
  const me = document.currentScript;
51
62
  const url = me.src || me.href; // might be <link> as well
52
63
  const params = new URL(new Request(url).url).searchParams;
53
64
  return trimObj({
54
- apiKey: params.get('api_key') || undefined
65
+ apiKey: params.get('api_key') || undefined,
55
66
  });
56
67
  }
68
+
57
69
  }
70
+
58
71
  MisoClient.helpers = helpers;
72
+
59
73
  defineValues(MisoClient, {
60
- uuid: uuidv4()
74
+ uuid: uuidv4(),
61
75
  });
62
- export default init(MisoClient);
76
+
77
+ export default init(MisoClient);
@@ -5,22 +5,21 @@ function execute(onError, fn) {
5
5
  onError(e);
6
6
  }
7
7
  }
8
- export default function (key) {
9
- let onError = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : e => console.error(e);
8
+
9
+ export default function(key, onError = e => console.error(e)) {
10
10
  const cmds = window[key] || (window[key] = []);
11
11
  if (cmds._processed) {
12
12
  return;
13
13
  }
14
14
  cmds._processed = true;
15
- const exec = fn => execute(onError, fn);
16
15
 
16
+ const exec = fn => execute(onError, fn);
17
+
17
18
  // overrides push function so future commands are executed immediately
18
- Object.defineProperty(cmds, 'push', {
19
- value: exec
20
- });
21
-
19
+ Object.defineProperty(cmds, 'push', { value: exec });
20
+
22
21
  // process existing miso commands
23
22
  for (const fn of cmds) {
24
23
  exec(fn);
25
24
  }
26
- }
25
+ }
@@ -1,4 +1,4 @@
1
1
  export const API = {
2
2
  DATA_ENDPOINT: 'https://api-edge.askmiso.com/v1',
3
- EVENT_ENDPOINT: 'https://api.askmiso.com/v1'
4
- };
3
+ EVENT_ENDPOINT: 'https://api.askmiso.com/v1',
4
+ };
@@ -1,7 +1,10 @@
1
1
  import { Component } from '@miso.ai/commons';
2
+
2
3
  export default class Context extends Component {
4
+
3
5
  constructor(client) {
4
6
  super('context', client);
5
7
  this._client = client;
6
8
  }
7
- }
9
+
10
+ }
package/src/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { default as version } from './version.js';
2
+ export { default as MisoClient } from './client.js';
3
+ export * from './plugin/index.js';
@@ -1,8 +1,9 @@
1
1
  import { defineValues } from '@miso.ai/commons';
2
+
2
3
  export default class AnonymousPlugin {
4
+
3
5
  constructor(install) {
4
- defineValues(this, {
5
- install
6
- });
6
+ defineValues(this, { install });
7
7
  }
8
- }
8
+
9
+ }
@@ -1,30 +1,31 @@
1
1
  import { Component } from '@miso.ai/commons';
2
+
2
3
  const PLUGIN_ID = 'std:auto-events';
4
+
3
5
  export default class AutoEventsPlugin extends Component {
6
+
4
7
  static get id() {
5
8
  return PLUGIN_ID;
6
9
  }
10
+
7
11
  constructor() {
8
12
  super('auto-events');
9
13
  this._contexts = new WeakMap();
10
14
  }
15
+
11
16
  install(MisoClient, context) {
12
17
  context.addSubtree(this);
13
18
  MisoClient.on('create', this._injectClient.bind(this));
14
19
  context.addPayloadPass(this._captureApiRequest.bind(this));
15
20
  }
21
+
16
22
  _injectClient(client) {
17
23
  const context = new AutoEventsContext(this);
18
24
  this._contexts.set(client, context);
19
25
  client.autoEvents = context.interface;
20
26
  }
21
- _captureApiRequest(_ref) {
22
- let {
23
- client,
24
- apiGroup,
25
- apiName,
26
- payload
27
- } = _ref;
27
+
28
+ _captureApiRequest({ client, apiGroup, apiName, payload }) {
28
29
  switch (apiGroup) {
29
30
  case 'search':
30
31
  switch (apiName) {
@@ -35,10 +36,8 @@ export default class AutoEventsPlugin extends Component {
35
36
  break;
36
37
  }
37
38
  }
38
- _captureSearchApiRequest(client, _ref2) {
39
- let {
40
- q: keywords
41
- } = _ref2;
39
+
40
+ _captureSearchApiRequest(client, { q: keywords }) {
42
41
  if (!keywords || keywords === '*') {
43
42
  // might be a facet search without real keywords
44
43
  return;
@@ -49,29 +48,26 @@ export default class AutoEventsPlugin extends Component {
49
48
  }
50
49
  client.api.interactions.upload({
51
50
  type: 'search',
52
- search: {
53
- keywords
54
- },
55
- context: {
56
- custom_context: {
57
- channel: 'miso_api'
58
- }
59
- }
51
+ search: { keywords },
52
+ context: { custom_context: { channel: 'miso_api' } },
60
53
  });
61
54
  }
55
+
62
56
  }
57
+
63
58
  class AutoEventsContext {
59
+
64
60
  constructor(plugin) {
65
61
  this._plugin = plugin;
66
62
  this._config = {
67
- search: true
63
+ search: true,
68
64
  };
69
65
  this.interface = Object.freeze({
70
- config: this.config.bind(this)
66
+ config: this.config.bind(this),
71
67
  });
72
68
  }
73
- config() {
74
- let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
69
+
70
+ config(options = {}) {
75
71
  if (typeof options !== 'object') {
76
72
  throw new Error(`Config options must be an object or undefined: ${options}`);
77
73
  }
@@ -86,6 +82,9 @@ class AutoEventsContext {
86
82
  this._plugin._events.emit('config', `${key}: ${oldValue} -> ${newValue}`);
87
83
  }
88
84
  }
89
- return Object.freeze(Object.assign({}, this._config));
85
+ return Object.freeze({
86
+ ...this._config,
87
+ });
90
88
  }
91
- }
89
+
90
+ }
@@ -0,0 +1,3 @@
1
+ export { default as UserPlugin } from './user.js';
2
+ export { default as PageInfoPlugin } from './page-info.js';
3
+ export { default as AutoEventsPlugin } from './auto-events.js';