@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 +28 -3
- package/index.js +2 -0
- package/package.json +1 -1
- package/src/ai.js +171 -0
- package/src/payments.js +2 -1
- package/src/utils.functional.js +2 -0
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
|
-
|
14
|
-
|
15
|
-
`
|
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
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
|
package/src/utils.functional.js
CHANGED
@@ -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
|
)
|