@storecraft/sdk 1.0.9 → 1.0.10

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/README.md CHANGED
@@ -10,9 +10,34 @@ which means you can you it both at browser and at backend runtimes such (`node`
10
10
 
11
11
  It will allow you to fetch / mutate all of the resources at the `backend` in a
12
12
  convenient manner with `javascript`, such as:
13
- `products`, `collections`, `authentication`, `customers`, `orders`, `discounts`,
14
- `storage`, `storefronts`, `shipping`, `statistics`, `tags`, `posts`, `notifications`,
15
- `templates`, `extensions` and more :)
13
+
14
+ #### collections
15
+ `products`, `collections`, `auth_users`, `customers`, `orders`, `discounts`,
16
+ `storefronts`, `shipping`, `tags`, `posts`, `notifications`,
17
+ `templates`, `extensions`, `images`
18
+
19
+ #### Auth
20
+ Perform authentication such as `signin` / `signup` / `api-key`
21
+
22
+ #### Checkout
23
+ Perform checkout `create` / `complete`
24
+
25
+ #### Storage
26
+ Perform storage operations such as `put` / `get` / `delete`
27
+
28
+ #### Payments
29
+ Perform payments `status-check` / `invoke-action`
30
+
31
+ #### Statistics
32
+ - Query some basic statistics about `orders` in a time span
33
+ - Query count of items in collections
34
+
35
+ #### AI
36
+ Speak with a `storecraft` agent (Supports streaming :))
37
+
38
+ <hr/>
39
+
40
+ Start by installing,
16
41
 
17
42
  Plus, everything is typed so you dont have to guess any parameter or queryable key
18
43
 
package/index.js CHANGED
@@ -16,6 +16,7 @@ import Payments from './src/payments.js'
16
16
  import Settings from './src/settings.js'
17
17
  import Notifications from './src/notifications.js'
18
18
  import Storage from './src/storage.js'
19
+ import AI from './src/ai.js'
19
20
 
20
21
 
21
22
  /**
@@ -49,6 +50,7 @@ export class StorecraftSDK {
49
50
  constructor(config) {
50
51
  this.#_config = config;
51
52
 
53
+ this.ai = new AI(this);
52
54
  this.auth = new Auth(this);
53
55
  this.storage = new Storage(this);
54
56
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storecraft/sdk",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "Official storecraft Universal Javascript SDK",
5
5
  "license": "MIT",
6
6
  "author": "Tomer Shalev (https://github.com/store-craft)",
package/src/ai.js ADDED
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @import {
3
+ * AgentRunParameters, AgentRunResponse
4
+ * } from '@storecraft/core/ai/agents/types.js'
5
+ * @import { content } from '@storecraft/core/ai/types.public.js'
6
+ */
7
+
8
+ import { HEADER_STORECRAFT_THREAD_ID } from '../../core/rest/con.ai.routes.js';
9
+ import { StorecraftSDK } from '../index.js'
10
+ import { url } from './utils.api.fetch.js';
11
+
12
+ /**
13
+ * @description **AI**
14
+ *
15
+ */
16
+ export default class AI {
17
+
18
+ /**
19
+ *
20
+ * @param {StorecraftSDK} sdk
21
+ */
22
+ constructor(sdk) {
23
+ this.sdk = sdk;
24
+ }
25
+
26
+ /**
27
+ * @description Speak with the main `storecraft` agent sync. It is
28
+ * recommended to use the streamed version {@link streamSpeak}
29
+ * @param {AgentRunParameters} params
30
+ * @returns {Promise<AgentRunResponse>}
31
+ */
32
+ speak = async (params) => {
33
+ const response = await fetch(
34
+ url(this.sdk.config, 'ai/agent/run'),
35
+ {
36
+ method: 'post',
37
+ body: JSON.stringify(params),
38
+ headers: {
39
+ 'Content-Type': 'application/json'
40
+ }
41
+ }
42
+ );
43
+
44
+ return response.json();
45
+ }
46
+
47
+ /**
48
+ * @description Stream Speak with the main `storecraft` agent via Server-Sent Events
49
+ * @param {AgentRunParameters} params
50
+ */
51
+ streamSpeak = async function(params) {
52
+ const response = await fetch(
53
+ url(this.sdk.config, 'ai/agent/stream'),
54
+ {
55
+ method: 'post',
56
+ body: JSON.stringify(params),
57
+ headers: {
58
+ 'Content-Type': 'application/json'
59
+ }
60
+ }
61
+ );
62
+
63
+ const threadId = response.headers.get(HEADER_STORECRAFT_THREAD_ID ?? 'X-Storecraft-Thread-Id');
64
+
65
+ if(!threadId) {
66
+ throw new Error(
67
+ `X-Storecraft-Thread-Id is missing, please tell the backend admin to
68
+ change the cors' Access-Control-Expose-Headers to accept the header`
69
+ )
70
+ }
71
+
72
+ return {
73
+ threadId,
74
+ generator: () => StreamSpeakGenerator(response.body)
75
+ }
76
+ }
77
+
78
+ }
79
+
80
+ const sleep = (ms=100) => {
81
+ return new Promise(
82
+ (resolve, reject) => {
83
+ setTimeout(
84
+ resolve, ms
85
+ )
86
+ }
87
+ )
88
+ }
89
+
90
+ /**
91
+ * @description Server Sent Events async generator
92
+ * @param {ReadableStream} stream web stream
93
+ */
94
+ const StreamSpeakGenerator = async function *(stream) {
95
+ for await (const sse of SSEGenerator(stream)) {
96
+ await sleep(50);
97
+ yield ( /** @type {content} */ (JSON.parse(sse.data)));
98
+ }
99
+ }
100
+
101
+ /**
102
+ * @description Server Sent Events async generator
103
+ * @param {ReadableStream} stream web stream
104
+ */
105
+ export const SSEGenerator = async function *(stream) {
106
+
107
+ let active_frame = [];
108
+ let residual_line = '';
109
+
110
+ const reader = stream.getReader();
111
+ let current = await reader.read();
112
+
113
+ while(!current.done) {
114
+
115
+ let text = (new TextDecoder()).decode(current.value);
116
+ // console.log('text \n\n', text)
117
+
118
+ if(residual_line) {
119
+ text = residual_line + text;
120
+ residual_line = '';
121
+ }
122
+
123
+ const lines = text.split(/\r\n|\n|\r/).map(l => l.trim());
124
+
125
+ for(const line of lines) {
126
+ if(line==='' && active_frame.length) {
127
+ // console.log('frame \n\n', active_frame)
128
+ // empty line means processing and dispatch
129
+ yield parse_frame(active_frame);
130
+ active_frame = [];
131
+ } else {
132
+ active_frame.push(line);
133
+ }
134
+ }
135
+
136
+ // if we got here and we have a line, then it
137
+ // was not finished (Otherwise, it would have been parsed and dispatched)
138
+ // I will need to prepend it to the next batch as it is incomplete
139
+ residual_line = active_frame.pop();
140
+
141
+ current = await reader.read();
142
+ }
143
+
144
+ }
145
+
146
+
147
+
148
+ /**
149
+ * @typedef {object} SSEFrame Server Sent Events frame
150
+ * @prop {string} [data]
151
+ * @prop {string} [event]
152
+ */
153
+
154
+ /**
155
+ *
156
+ * @param {string[]} lines
157
+ * @returns {SSEFrame}
158
+ */
159
+ const parse_frame = (lines) => {
160
+ return Object.fromEntries(
161
+ lines.map(
162
+ (l) => {
163
+ const delimiter = l.indexOf(':');
164
+ return [
165
+ l.slice(0, delimiter).trim(),
166
+ l.slice(delimiter + 1).trim(),
167
+ ]
168
+ }
169
+ )
170
+ );
171
+ }
package/src/payments.js CHANGED
@@ -62,7 +62,8 @@ export default class Payments {
62
62
 
63
63
  /**
64
64
  *
65
- * Invoke a `payment gateway` action on `order`
65
+ * Invoke a `payment gateway` action on `order`. The list of available actions can be found
66
+ * using {@link get} or {@link paymentStatusOfOrder}
66
67
  *
67
68
  *
68
69
  * @param {string} action_handle The `action` handle at the gateway
@@ -89,7 +89,9 @@ export const text2tokens = (text) => {
89
89
  // let tokens = text?.toString().toLowerCase().match(/[^\W_]+/g)
90
90
  let tokens = text?.toString().toLowerCase().match(/[\p{L}\d]+/gu)
91
91
 
92
+ // @ts-ignore
92
93
  tokens = tokens ?? []
94
+ // @ts-ignore
93
95
  tokens = tokens.filter(
94
96
  t => !STOP_WORDS.includes(t)
95
97
  )