@storecraft/sdk 1.0.8 → 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 +53 -7
- package/index.js +2 -0
- package/package.json +2 -1
- package/src/ai.js +171 -0
- package/src/auth.js +25 -17
- package/src/payments.js +2 -1
- package/src/utils.api.fetch.js +2 -2
- package/src/utils.functional.js +2 -0
package/README.md
CHANGED
@@ -10,9 +10,36 @@ 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,
|
41
|
+
|
42
|
+
Plus, everything is typed so you dont have to guess any parameter or queryable key
|
16
43
|
|
17
44
|
```bash
|
18
45
|
npm i @storecraft/sdk
|
@@ -111,10 +138,14 @@ const auth_result = await sdk.auth.signout();
|
|
111
138
|
## Querying
|
112
139
|
|
113
140
|
|
114
|
-
Here are some examples for querying
|
141
|
+
Here are some examples for querying.
|
115
142
|
|
143
|
+
- Every key and string in the example below is fully typed with `typescript`,
|
144
|
+
- so you get **intellisense** out of the box for better developer experience
|
145
|
+
- And, you don't have to guess anything
|
116
146
|
|
117
|
-
|
147
|
+
|
148
|
+
```ts
|
118
149
|
import { StorecraftSDK } from '@storecraft/sdk'
|
119
150
|
|
120
151
|
const sdk = new StorecraftSDK();
|
@@ -122,10 +153,10 @@ const sdk = new StorecraftSDK();
|
|
122
153
|
const products: ProductType[] = await sdk.products.list(
|
123
154
|
{
|
124
155
|
expand: ['collections', 'variants'],
|
125
|
-
sortBy: ['updated_at', 'id'],
|
156
|
+
sortBy: ['updated_at', 'id'], // all keys will show up in intellisense
|
126
157
|
order: 'desc',
|
127
158
|
startAt: [
|
128
|
-
['updated_at': '2024-03-24']
|
159
|
+
['updated_at': '2024-03-24'],
|
129
160
|
],
|
130
161
|
limit: 5,
|
131
162
|
vql: '(keyword1 | keyword2) -(keyword3)'
|
@@ -134,6 +165,21 @@ const products: ProductType[] = await sdk.products.list(
|
|
134
165
|
|
135
166
|
```
|
136
167
|
|
168
|
+
Or,
|
169
|
+
|
170
|
+
```ts
|
171
|
+
import { StorecraftSDK } from '@storecraft/sdk'
|
172
|
+
|
173
|
+
const sdk = new StorecraftSDK();
|
174
|
+
|
175
|
+
const collections: CollectionType[] = await sdk.collections.list(
|
176
|
+
{
|
177
|
+
equals: [['active': true]]
|
178
|
+
limit: 5,
|
179
|
+
}
|
180
|
+
);
|
181
|
+
|
182
|
+
```
|
137
183
|
|
138
184
|
```text
|
139
185
|
Author: Tomer Shalev (tomer.shalev@gmail.com)
|
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.
|
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)",
|
@@ -19,6 +19,7 @@
|
|
19
19
|
"scripts": {
|
20
20
|
"sdk:test": "echo \"Error: no test specified\" && exit 1",
|
21
21
|
"test": "npm run sdk:test",
|
22
|
+
"sc-publish": "npm publish",
|
22
23
|
"prepublishOnly": "npm version patch --force"
|
23
24
|
},
|
24
25
|
"type": "module",
|
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/auth.js
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
/**
|
2
|
+
* @import {
|
3
|
+
* ApiAuthChangePasswordType, ApiAuthResult, ApiAuthSigninType, ApiAuthSignupType,
|
4
|
+
* ApiKeyResult, ApiQuery, AuthUserType, error
|
5
|
+
* } from '@storecraft/core/api'
|
6
|
+
*/
|
7
|
+
|
1
8
|
import { api_query_to_searchparams } from '@storecraft/core/api/utils.query.js';
|
2
9
|
import { StorecraftSDK } from '../index.js';
|
3
10
|
import { fetchApiWithAuth, url } from './utils.api.fetch.js';
|
@@ -136,7 +143,7 @@ export default class Auth {
|
|
136
143
|
}
|
137
144
|
);
|
138
145
|
|
139
|
-
/** @type {
|
146
|
+
/** @type {ApiAuthResult} */
|
140
147
|
let payload = undefined;
|
141
148
|
|
142
149
|
if(auth_res.ok) {
|
@@ -203,7 +210,7 @@ export default class Auth {
|
|
203
210
|
|
204
211
|
/**
|
205
212
|
*
|
206
|
-
* @param {
|
213
|
+
* @param {ApiAuthResult} user
|
207
214
|
*/
|
208
215
|
#_update_and_notify_subscribers = (user) => {
|
209
216
|
this.currentAuth = user
|
@@ -217,12 +224,12 @@ export default class Auth {
|
|
217
224
|
* @param {string} password
|
218
225
|
*
|
219
226
|
*
|
220
|
-
* @returns {Promise<
|
227
|
+
* @returns {Promise<ApiAuthResult>}
|
221
228
|
*/
|
222
229
|
signin = async (email, password) => {
|
223
230
|
// console.log('ep ', email, password)
|
224
231
|
|
225
|
-
/** @type {
|
232
|
+
/** @type {ApiAuthSigninType} */
|
226
233
|
const info = {
|
227
234
|
email, password
|
228
235
|
}
|
@@ -239,7 +246,7 @@ export default class Auth {
|
|
239
246
|
);
|
240
247
|
|
241
248
|
if(!res.ok) {
|
242
|
-
/** @type {
|
249
|
+
/** @type {error} */
|
243
250
|
let error_payload = {
|
244
251
|
messages: [
|
245
252
|
{
|
@@ -257,7 +264,7 @@ export default class Auth {
|
|
257
264
|
}
|
258
265
|
// assert(res.ok, 'auth/error2');
|
259
266
|
|
260
|
-
/** @type {
|
267
|
+
/** @type {ApiAuthResult} */
|
261
268
|
const payload = await res.json();
|
262
269
|
|
263
270
|
// console.log('auth_result', payload)
|
@@ -278,7 +285,7 @@ export default class Auth {
|
|
278
285
|
* @param {string} [lastname]
|
279
286
|
*/
|
280
287
|
signup = async (email, password, firstname, lastname) => {
|
281
|
-
/** @type {
|
288
|
+
/** @type {ApiAuthSignupType} */
|
282
289
|
const info = {
|
283
290
|
email, password,
|
284
291
|
firstname, lastname
|
@@ -297,7 +304,7 @@ export default class Auth {
|
|
297
304
|
|
298
305
|
assert(res.ok, 'auth/error');
|
299
306
|
|
300
|
-
/** @type {
|
307
|
+
/** @type {ApiAuthResult} */
|
301
308
|
const payload = await res.json();
|
302
309
|
|
303
310
|
this.#_update_and_notify_subscribers(
|
@@ -310,7 +317,7 @@ export default class Auth {
|
|
310
317
|
|
311
318
|
/**
|
312
319
|
*
|
313
|
-
* @param {
|
320
|
+
* @param {ApiAuthChangePasswordType} params
|
314
321
|
*/
|
315
322
|
|
316
323
|
changePassword = async (params) => {
|
@@ -327,7 +334,7 @@ export default class Auth {
|
|
327
334
|
);
|
328
335
|
|
329
336
|
if(!res.ok) {
|
330
|
-
/** @type {
|
337
|
+
/** @type {error} */
|
331
338
|
let error_payload = {
|
332
339
|
messages: [
|
333
340
|
{
|
@@ -344,7 +351,7 @@ export default class Auth {
|
|
344
351
|
throw error_payload;
|
345
352
|
}
|
346
353
|
|
347
|
-
/** @type {
|
354
|
+
/** @type {ApiAuthResult} */
|
348
355
|
const payload = await res.json();
|
349
356
|
|
350
357
|
this.#_update_and_notify_subscribers(
|
@@ -367,7 +374,7 @@ export default class Auth {
|
|
367
374
|
|
368
375
|
|
369
376
|
create_api_key = async () => {
|
370
|
-
/** @type {
|
377
|
+
/** @type {ApiKeyResult} */
|
371
378
|
const item = await fetchApiWithAuth(
|
372
379
|
this.#sdk,
|
373
380
|
'/auth/apikeys',
|
@@ -386,7 +393,7 @@ export default class Auth {
|
|
386
393
|
* @param {string} email_or_id
|
387
394
|
*/
|
388
395
|
get_auth_user = async (email_or_id) => {
|
389
|
-
/** @type {
|
396
|
+
/** @type {AuthUserType} */
|
390
397
|
const item = await fetchApiWithAuth(
|
391
398
|
this.#sdk,
|
392
399
|
`/auth/users/${email_or_id}`,
|
@@ -417,25 +424,26 @@ export default class Auth {
|
|
417
424
|
/**
|
418
425
|
*
|
419
426
|
*
|
420
|
-
* @param {
|
427
|
+
* @param {ApiQuery<AuthUserType>} query
|
421
428
|
*
|
422
429
|
*/
|
423
430
|
list_auth_users = async (query) => {
|
424
431
|
const sq = api_query_to_searchparams(query);
|
425
|
-
|
426
|
-
|
432
|
+
/** @type {AuthUserType[]} */
|
433
|
+
const items = await fetchApiWithAuth(
|
427
434
|
this.#sdk,
|
428
435
|
`/auth/users?${sq.toString()}`,
|
429
436
|
{
|
430
437
|
method: 'get'
|
431
438
|
}
|
432
439
|
);
|
440
|
+
return items;
|
433
441
|
}
|
434
442
|
|
435
443
|
|
436
444
|
list_api_keys_auth_users = async () => {
|
437
445
|
|
438
|
-
/** @type {
|
446
|
+
/** @type {AuthUserType[]} */
|
439
447
|
const items = await fetchApiWithAuth(
|
440
448
|
this.#sdk,
|
441
449
|
'/auth/apikeys',
|
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.api.fetch.js
CHANGED
@@ -183,7 +183,7 @@ export async function remove(sdk, resource, handle_or_id) {
|
|
183
183
|
*
|
184
184
|
* @param {import('../index.js').StorecraftSDK} sdk
|
185
185
|
* @param {string} resource base path of resource
|
186
|
-
* @param {import('@storecraft/core/api').ApiQuery} [query]
|
186
|
+
* @param {import('@storecraft/core/api').ApiQuery<G>} [query]
|
187
187
|
*
|
188
188
|
*
|
189
189
|
* @returns {Promise<G[]>}
|
@@ -262,7 +262,7 @@ export class collection_base {
|
|
262
262
|
|
263
263
|
/**
|
264
264
|
*
|
265
|
-
* @param {import('@storecraft/core/api').ApiQuery} query Query object
|
265
|
+
* @param {import('@storecraft/core/api').ApiQuery<G>} query Query object
|
266
266
|
*
|
267
267
|
*
|
268
268
|
* @returns {Promise<G[]>}
|
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
|
)
|