@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 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
- `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,
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
- ```js
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.8",
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 {import('@storecraft/core/api').ApiAuthResult} */
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 {import('@storecraft/core/api').ApiAuthResult} user
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<import('@storecraft/core/api').ApiAuthResult>}
227
+ * @returns {Promise<ApiAuthResult>}
221
228
  */
222
229
  signin = async (email, password) => {
223
230
  // console.log('ep ', email, password)
224
231
 
225
- /** @type {import('@storecraft/core/api').ApiAuthSigninType} */
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 {import('@storecraft/core/api').error} */
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 {import('@storecraft/core/api').ApiAuthResult} */
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 {import('@storecraft/core/api').ApiAuthSignupType} */
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 {import('@storecraft/core/api').ApiAuthResult} */
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 {import('@storecraft/core/api').ApiAuthChangePasswordType} params
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 {import('@storecraft/core/api').error} */
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 {import('@storecraft/core/api').ApiAuthResult} */
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 {import('@storecraft/core/api').ApiKeyResult} */
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 {import('@storecraft/core/api').AuthUserType} */
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 {import('@storecraft/core/api').ApiQuery} query
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
- return fetchApiWithAuth(
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 {import('@storecraft/core/api').AuthUserType[]} */
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
@@ -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[]>}
@@ -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
  )