sizebay-core-sdk 1.1.4 → 1.2.0
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 +196 -1
- package/dist/helpers/http-helper.d.ts +6 -0
- package/dist/modules/ai-image-service.d.ts +3 -1
- package/dist/modules/index.d.ts +2 -1
- package/dist/modules/sessions.d.ts +11 -0
- package/dist/sizebay-core-sdk.es.js +202 -79
- package/dist/sizebay-core-sdk.umd.js +1 -1
- package/dist/types/ai-image-service.types.d.ts +31 -0
- package/dist/types/sessions.d.ts +23 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -227,4 +227,199 @@ async function fetchRecommendedSizes() {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
fetchRecommendedSizes();
|
|
230
|
-
```
|
|
230
|
+
```
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
### `getComplementaryProducts(params: GetComplementaryProductsParams): Promise<GetComplementaryProductsResponse>`
|
|
236
|
+
|
|
237
|
+
Fetches complementary product recommendation pairs based on the given parameters.
|
|
238
|
+
|
|
239
|
+
#### Parameters
|
|
240
|
+
|
|
241
|
+
| Parameter | Type | Required | Description |
|
|
242
|
+
|------------------|----------|:--------:|--------------------------------------------------------------------|
|
|
243
|
+
| `tenantId` | `string` | **Yes** | The tenant ID that initiated the request. |
|
|
244
|
+
| `collectionName` | `string` | **Yes** | The Qdrant collection name where embeddings are stored. |
|
|
245
|
+
| `sessionId` | `string` | **Yes** | The session ID for internal event tracking. |
|
|
246
|
+
| `permalink` | `string` | **Yes** | A permanent link representing the base product. |
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
#### Returns
|
|
250
|
+
|
|
251
|
+
**`Promise<GetComplementaryProductsResponse>`**
|
|
252
|
+
An object containing:
|
|
253
|
+
|
|
254
|
+
- `baseProduct`: The original product used as the seed for recommendations (`ProductResponseDto`).
|
|
255
|
+
- `complementary`: An array of recommendation pairs (`ComplementaryPairDto[]`), each with:
|
|
256
|
+
- `first`: The first complementary product.
|
|
257
|
+
- `secondary`: The second complementary product.
|
|
258
|
+
|
|
259
|
+
#### Example
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { GetComplementaryProductsParams } from 'sizebay-core-sdk';
|
|
263
|
+
|
|
264
|
+
const params: GetComplementaryProductsParams = {
|
|
265
|
+
tenantId: '123',
|
|
266
|
+
collectionName: 'summer-collection',
|
|
267
|
+
sessionId: 'abc-session-456',
|
|
268
|
+
permalink: 'https://example.com/product/base-1'
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
async function fetchComplementaryPairs() {
|
|
272
|
+
try {
|
|
273
|
+
const response = await client.getComplementaryProducts(params);
|
|
274
|
+
console.log('Base product:', response.baseProduct);
|
|
275
|
+
response.complementary.forEach(({ first, secondary }) => {
|
|
276
|
+
console.log(`Pair: ${first.title} ↔ ${secondary.title}`);
|
|
277
|
+
});
|
|
278
|
+
} catch (error: any) {
|
|
279
|
+
console.error('Error fetching complementary products:', error.message);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
fetchComplementaryPairs();
|
|
284
|
+
```
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Session Module
|
|
288
|
+
|
|
289
|
+
### `getSessionInfo(): Promise<SessionContext>`
|
|
290
|
+
|
|
291
|
+
Retrieves (or reuses) the session context, containing:
|
|
292
|
+
- `sid` – the `catalogUser.id` returned by the endpoint
|
|
293
|
+
- `sessionId` – the numeric session identifier
|
|
294
|
+
|
|
295
|
+
If not already cached, it fetches both values from the session endpoint.
|
|
296
|
+
|
|
297
|
+
#### Returns
|
|
298
|
+
|
|
299
|
+
- A `Promise<SessionContext>` that resolves to an object with `sid` (string) and `sessionId` (number).
|
|
300
|
+
|
|
301
|
+
#### Example
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
const { sid, sessionId } = await client.getSessionInfo();
|
|
305
|
+
console.log('Catalog SID:', sid);
|
|
306
|
+
console.log('Numeric sessionId:', sessionId);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
### `sendProfile(payload: SessionProfilePayload, sid?: string): Promise<void>`
|
|
312
|
+
|
|
313
|
+
Sends a user profile to the server. The profile is associated with the provided `sid`, or falls back to the internally stored one (via `getSessionInfo()`).
|
|
314
|
+
|
|
315
|
+
#### Parameters
|
|
316
|
+
|
|
317
|
+
| Parameter | Type | Required | Description |
|
|
318
|
+
|-----------|----------------------------|----------|-----------------------------------------------------------------------------------------------|
|
|
319
|
+
| `payload` | `SessionProfilePayload` | Yes | An object containing user profile details. |
|
|
320
|
+
| `sid` | `string` | No | The `catalogUser.id` to use. If omitted, the method will retrieve it via `getSessionInfo()`. |
|
|
321
|
+
|
|
322
|
+
#### `SessionProfilePayload` Structure
|
|
323
|
+
|
|
324
|
+
| Field | Type | Required | Description |
|
|
325
|
+
|-------------------|---------------------------------------------------|----------|--------------------------------------------|
|
|
326
|
+
| `userId` | `string` | Yes | The user’s unique ID |
|
|
327
|
+
| `name` | `string` | Yes | The profile name |
|
|
328
|
+
| `skinType` | `number` | Yes | Skin type category |
|
|
329
|
+
| `footShape` | `string \| null` | No | Optional foot shape |
|
|
330
|
+
| `gender` | `string` | Yes | User’s gender (`'M'` or `'F'`) |
|
|
331
|
+
| `age` | `string` | Yes | User’s age as a string |
|
|
332
|
+
| `is3dFeel` | `boolean` | Yes | Whether 3D feel is enabled |
|
|
333
|
+
| `weight` | `string` | Yes | User’s weight in string format |
|
|
334
|
+
| `height` | `string` | Yes | User’s height in string format |
|
|
335
|
+
| `measures` | `{ insoleLength: number; poundWeight: number\|null }` | Yes | Additional measurements |
|
|
336
|
+
| `product` | `string \| null` | No | Optional product context |
|
|
337
|
+
| `isMetric` | `boolean` | Yes | Whether measurements use the metric system |
|
|
338
|
+
| `bodyShapeChest` | `number` | Yes | Chest shape index |
|
|
339
|
+
| `bodyShapeWaist` | `number` | Yes | Waist shape index |
|
|
340
|
+
| `bodyShapeHip` | `number` | Yes | Hip shape index |
|
|
341
|
+
|
|
342
|
+
#### Example
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
const profile: SessionProfilePayload = {
|
|
346
|
+
userId: 'abc123',
|
|
347
|
+
name: 'szb-profile-no-name',
|
|
348
|
+
skinType: 0,
|
|
349
|
+
footShape: null,
|
|
350
|
+
gender: 'F',
|
|
351
|
+
age: '25',
|
|
352
|
+
is3dFeel: true,
|
|
353
|
+
weight: '80',
|
|
354
|
+
height: '180',
|
|
355
|
+
measures: { insoleLength: 0, poundWeight: null },
|
|
356
|
+
product: null,
|
|
357
|
+
isMetric: false,
|
|
358
|
+
bodyShapeChest: 0,
|
|
359
|
+
bodyShapeWaist: 0,
|
|
360
|
+
bodyShapeHip: 0,
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
await client.sendProfile(profile);
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
💡 **React Tip:** To avoid recreating the SDK on every render, memoize your client with `useMemo`:
|
|
367
|
+
|
|
368
|
+
```tsx
|
|
369
|
+
import { useMemo, useState } from 'react';
|
|
370
|
+
import { createClient } from 'sizebay-core-sdk';
|
|
371
|
+
|
|
372
|
+
export function MyComponent() {
|
|
373
|
+
// client is only created once on mount
|
|
374
|
+
const client = useMemo(() => createClient({ env: 'development' }), []);
|
|
375
|
+
|
|
376
|
+
const [profileStatus, setProfileStatus] = useState<string | null>(null);
|
|
377
|
+
|
|
378
|
+
const handleGetSessionInfo = async () => {
|
|
379
|
+
const { sid } = await client.getSessionInfo();
|
|
380
|
+
// …use sid…
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const handleSendProfile = async () => {
|
|
384
|
+
await client.sendProfile(profilePayload);
|
|
385
|
+
setProfileStatus('Profile sent successfully!');
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// …
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
💡 **Framework-Agnostic Tip:** Expose a singleton so every part of your app shares the same instance:
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
// sdk.ts
|
|
398
|
+
import { createClient } from 'sizebay-core-sdk';
|
|
399
|
+
import type { ClientType } from '@src/types';
|
|
400
|
+
|
|
401
|
+
let _client: ClientType | null = null;
|
|
402
|
+
export function getClient(): ClientType {
|
|
403
|
+
if (!_client) {
|
|
404
|
+
_client = createClient({ env: 'development' });
|
|
405
|
+
}
|
|
406
|
+
return _client;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// anywhere in your app
|
|
410
|
+
import { getClient } from './sdk';
|
|
411
|
+
const client = getClient();
|
|
412
|
+
await client.getSessionInfo();
|
|
413
|
+
await client.sendProfile(profilePayload);
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
💡 **Extra:** If you ever need to bypass the internal cache, you can pass the `sid` manually:
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
await client.sendProfile(
|
|
422
|
+
profilePayload,
|
|
423
|
+
'028BC4AF5791a68fbbf8c48b4300a6a3319165552a9d'
|
|
424
|
+
);
|
|
425
|
+
```
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Config } from '../config';
|
|
2
|
-
import { GetRecommendedSizeByProductsParams, GetRecommendedSizeByProductsResponse,
|
|
2
|
+
import { GetSimilarProductsParams, GetSimilarProductsResponse, GetRecommendedSizeByProductsParams, GetRecommendedSizeByProductsResponse, GetComplementaryProductsParams, GetComplementaryProductsResponse } from '../types/ai-image-service.types';
|
|
3
3
|
export declare class AIImageService {
|
|
4
4
|
private endpoint;
|
|
5
5
|
constructor(config: Config);
|
|
6
|
+
private appendQueryParams;
|
|
6
7
|
getSimilarProducts(params: GetSimilarProductsParams): Promise<GetSimilarProductsResponse>;
|
|
7
8
|
getRecommendedSizeByProducts(payload: GetRecommendedSizeByProductsParams): Promise<GetRecommendedSizeByProductsResponse[]>;
|
|
9
|
+
getComplementaryProducts(params: GetComplementaryProductsParams): Promise<GetComplementaryProductsResponse>;
|
|
8
10
|
}
|
package/dist/modules/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Tracker } from './tracker';
|
|
2
2
|
import { AIImageService } from './ai-image-service';
|
|
3
|
-
|
|
3
|
+
import { SessionManager } from './sessions';
|
|
4
|
+
export declare const moduleClasses: (typeof Tracker | typeof AIImageService | typeof SessionManager)[];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Config } from '../config';
|
|
2
|
+
import { SessionContext, SessionProfilePayload } from '../types/sessions';
|
|
3
|
+
export declare class SessionManager {
|
|
4
|
+
private sessionEndpoint;
|
|
5
|
+
private profileEndpoint;
|
|
6
|
+
private sid;
|
|
7
|
+
private sessionId;
|
|
8
|
+
constructor(config: Config);
|
|
9
|
+
getSessionInfo(): Promise<SessionContext>;
|
|
10
|
+
sendProfile(payload: SessionProfilePayload, sid?: string): Promise<void>;
|
|
11
|
+
}
|
|
@@ -6,47 +6,51 @@ const c = {
|
|
|
6
6
|
aiImageService: {
|
|
7
7
|
production: "https://ai-image-service.internalsizebay.com",
|
|
8
8
|
development: "https://ai-image-service-dev.internalsizebay.com"
|
|
9
|
+
},
|
|
10
|
+
session: {
|
|
11
|
+
production: "https://vfr-v3-production.sizebay.technology/api/me",
|
|
12
|
+
development: "https://vfr-v3-staging.sizebay.eu/api/me"
|
|
9
13
|
}
|
|
10
14
|
// Adicione outros serviços conforme necessário
|
|
11
15
|
};
|
|
12
|
-
class
|
|
13
|
-
constructor(
|
|
14
|
-
const
|
|
15
|
-
this.serviceOverrides =
|
|
16
|
-
for (const
|
|
17
|
-
if (Object.prototype.hasOwnProperty.call(c,
|
|
18
|
-
const o = c[
|
|
16
|
+
class l {
|
|
17
|
+
constructor(t) {
|
|
18
|
+
const e = t.env || "development";
|
|
19
|
+
this.serviceOverrides = t.services || {}, this.endpoints = {};
|
|
20
|
+
for (const s in c)
|
|
21
|
+
if (Object.prototype.hasOwnProperty.call(c, s)) {
|
|
22
|
+
const o = c[s][e];
|
|
19
23
|
if (!o)
|
|
20
24
|
continue;
|
|
21
|
-
this.endpoints[
|
|
25
|
+
this.endpoints[s] = o;
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
|
-
getEndpoint(
|
|
25
|
-
const
|
|
26
|
-
if (!
|
|
28
|
+
getEndpoint(t) {
|
|
29
|
+
const e = this.endpoints[t];
|
|
30
|
+
if (!e)
|
|
27
31
|
throw new Error(
|
|
28
|
-
`Endpoint for service '${
|
|
32
|
+
`Endpoint for service '${t}' is not configured.`
|
|
29
33
|
);
|
|
30
|
-
return
|
|
34
|
+
return e;
|
|
31
35
|
}
|
|
32
|
-
getServiceConfig(
|
|
33
|
-
return this.serviceOverrides[
|
|
36
|
+
getServiceConfig(t) {
|
|
37
|
+
return this.serviceOverrides[t] || {};
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
|
-
class
|
|
37
|
-
constructor(
|
|
38
|
-
this.endpoint =
|
|
39
|
-
}
|
|
40
|
-
async track(
|
|
41
|
-
const
|
|
42
|
-
eventName:
|
|
43
|
-
...
|
|
44
|
-
},
|
|
40
|
+
class u {
|
|
41
|
+
constructor(t) {
|
|
42
|
+
this.endpoint = t.getEndpoint("tracker");
|
|
43
|
+
}
|
|
44
|
+
async track(t, e) {
|
|
45
|
+
const s = {
|
|
46
|
+
eventName: t,
|
|
47
|
+
...e
|
|
48
|
+
}, n = new URL(`${this.endpoint}/events`);
|
|
45
49
|
try {
|
|
46
|
-
const o = await fetch(
|
|
50
|
+
const o = await fetch(n, {
|
|
47
51
|
method: "POST",
|
|
48
52
|
headers: { "Content-Type": "application/json" },
|
|
49
|
-
body: JSON.stringify(
|
|
53
|
+
body: JSON.stringify(s)
|
|
50
54
|
});
|
|
51
55
|
if (!o.ok) {
|
|
52
56
|
const i = await o.text();
|
|
@@ -58,67 +62,186 @@ class p {
|
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
|
-
class
|
|
62
|
-
constructor(
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
async getSimilarProducts(r) {
|
|
66
|
-
const n = new URL(`${this.endpoint}/recommendations/similar`);
|
|
67
|
-
Object.entries(r).forEach(([t, e]) => {
|
|
68
|
-
e !== void 0 && n.searchParams.append(t, String(e));
|
|
69
|
-
});
|
|
65
|
+
class a extends Error {
|
|
66
|
+
constructor(t, e) {
|
|
67
|
+
let s;
|
|
70
68
|
try {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
if (!t.ok) {
|
|
78
|
-
const e = await t.text();
|
|
79
|
-
throw new Error(`Request error: ${t.status} - ${e}`);
|
|
80
|
-
}
|
|
81
|
-
return await t.json();
|
|
82
|
-
} catch (t) {
|
|
83
|
-
throw new Error(`Error fetching similar products: ${t.message}`);
|
|
69
|
+
const n = JSON.parse(e);
|
|
70
|
+
s = (n == null ? void 0 : n.message) || e;
|
|
71
|
+
} catch {
|
|
72
|
+
s = e;
|
|
84
73
|
}
|
|
74
|
+
super(s), this.statusCode = t, this.details = e, Object.setPrototypeOf(this, a.prototype);
|
|
85
75
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
}
|
|
77
|
+
async function d(r, t) {
|
|
78
|
+
let e;
|
|
79
|
+
try {
|
|
80
|
+
e = await fetch(r, t);
|
|
81
|
+
} catch (n) {
|
|
82
|
+
throw new a(0, n.message);
|
|
83
|
+
}
|
|
84
|
+
const s = await e.text();
|
|
85
|
+
if (!e.ok)
|
|
86
|
+
throw new a(e.status, s);
|
|
87
|
+
return JSON.parse(s);
|
|
88
|
+
}
|
|
89
|
+
class f {
|
|
90
|
+
constructor(t) {
|
|
91
|
+
this.endpoint = t.getEndpoint("aiImageService");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Appends each non-null/undefined param to the given URL's query string.
|
|
95
|
+
* @param url - The URL object to modify.
|
|
96
|
+
* @param params - A record of key/value pairs to serialize.
|
|
97
|
+
*/
|
|
98
|
+
appendQueryParams(t, e) {
|
|
99
|
+
Object.entries(e).forEach(([s, n]) => {
|
|
100
|
+
n != null && t.searchParams.append(s, String(n));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Fetches similar product recommendations.
|
|
105
|
+
*
|
|
106
|
+
* Sends a GET to `/recommendations/similar` with the given query parameters.
|
|
107
|
+
*
|
|
108
|
+
* @param params - parameters for the request:
|
|
109
|
+
* - tenantId: string
|
|
110
|
+
* - collectionName: string
|
|
111
|
+
* - sessionId: string | number
|
|
112
|
+
* - permalink: string
|
|
113
|
+
* - page?: number
|
|
114
|
+
* - perPage?: number
|
|
115
|
+
* @returns Promise resolving to the parsed JSON:
|
|
116
|
+
* {@link GetSimilarProductsResponse}
|
|
117
|
+
* @throws {ApiError} if the HTTP status is not 2xx or on network failure
|
|
118
|
+
*/
|
|
119
|
+
getSimilarProducts(t) {
|
|
120
|
+
const e = new URL(`${this.endpoint}/recommendations/similar`);
|
|
121
|
+
return this.appendQueryParams(e, t), d(e.toString(), {
|
|
122
|
+
method: "GET",
|
|
123
|
+
headers: { "Content-Type": "application/json" }
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Fetches recommended sizes for a list of products.
|
|
128
|
+
*
|
|
129
|
+
* Sends a POST to `/recommendations/size-by-products` with the payload.
|
|
130
|
+
*
|
|
131
|
+
* @param payload - request payload:
|
|
132
|
+
* - tenantId: number
|
|
133
|
+
* - sid: string
|
|
134
|
+
* - sizeSystem: string
|
|
135
|
+
* - permalinks: string[]
|
|
136
|
+
* @returns Promise resolving to the parsed JSON array:
|
|
137
|
+
* {@link GetRecommendedSizeByProductsResponse}[]
|
|
138
|
+
* @throws {ApiError} if the HTTP status is not 2xx or on network failure
|
|
139
|
+
*/
|
|
140
|
+
getRecommendedSizeByProducts(t) {
|
|
141
|
+
const e = `${this.endpoint}/recommendations/size-by-products`;
|
|
142
|
+
return d(e, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: { "Content-Type": "application/json" },
|
|
145
|
+
body: JSON.stringify(t)
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Fetches complementary product recommendation pairs.
|
|
150
|
+
*
|
|
151
|
+
* Sends a GET to `/recommendations/complementary` with the given query parameters.
|
|
152
|
+
*
|
|
153
|
+
* @param params - parameters for the request:
|
|
154
|
+
* - tenantId: string
|
|
155
|
+
* - collectionName: string
|
|
156
|
+
* - sessionId: string
|
|
157
|
+
* - permalink: string
|
|
158
|
+
* - limit?: number
|
|
159
|
+
* @returns Promise resolving to the parsed JSON:
|
|
160
|
+
* {@link GetComplementaryProductsResponse}
|
|
161
|
+
* @throws {ApiError} if the HTTP status is not 2xx or on network failure
|
|
162
|
+
*/
|
|
163
|
+
getComplementaryProducts(t) {
|
|
164
|
+
const e = new URL(`${this.endpoint}/recommendations/complementary`);
|
|
165
|
+
return this.appendQueryParams(e, t), d(e.toString(), {
|
|
166
|
+
method: "GET",
|
|
167
|
+
headers: { "Content-Type": "application/json" }
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
class m {
|
|
172
|
+
constructor(t) {
|
|
173
|
+
this.sid = null, this.sessionId = null;
|
|
174
|
+
const e = t.getEndpoint("session");
|
|
175
|
+
this.sessionEndpoint = `${e}/`, this.profileEndpoint = `${e}/user/profile`;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Retrieves (or reuses) the session context, containing:
|
|
179
|
+
* - sid := user_id
|
|
180
|
+
* - sessionId
|
|
181
|
+
*
|
|
182
|
+
* If not already cached, it will fetch them from the session endpoint.
|
|
183
|
+
*
|
|
184
|
+
* @returns {Promise<SessionContext>} The session context with sid and sessionId
|
|
185
|
+
* @throws {Error} If the HTTP request fails
|
|
186
|
+
*/
|
|
187
|
+
async getSessionInfo() {
|
|
188
|
+
if (this.sid && this.sessionId !== null)
|
|
189
|
+
return { sid: this.sid, sessionId: this.sessionId };
|
|
190
|
+
const t = await fetch(this.sessionEndpoint, {
|
|
191
|
+
credentials: "include"
|
|
192
|
+
});
|
|
193
|
+
if (!t.ok) {
|
|
194
|
+
const s = await t.text();
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Failed to fetch session info: ${t.status} – ${s}`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const e = await t.json();
|
|
200
|
+
return this.sid = e.catalogUser.id, this.sessionId = e.sessionId, { sid: this.sid, sessionId: this.sessionId };
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Sends the session profile payload to the server.
|
|
204
|
+
*
|
|
205
|
+
* @param payload – the profile data to send
|
|
206
|
+
* @param sid – optional: if you already have the sid, pass it here;
|
|
207
|
+
* otherwise it will be retrieved via getSessionInfo()
|
|
208
|
+
* @throws {Error} If the HTTP request fails
|
|
209
|
+
*/
|
|
210
|
+
async sendProfile(t, e) {
|
|
211
|
+
const s = e ?? (await this.getSessionInfo()).sid, n = new URL(this.profileEndpoint);
|
|
212
|
+
n.searchParams.set("sid", s);
|
|
213
|
+
const o = await fetch(n.toString(), {
|
|
214
|
+
credentials: "include",
|
|
215
|
+
method: "POST",
|
|
216
|
+
headers: { "Content-Type": "application/json" },
|
|
217
|
+
body: JSON.stringify({ userId: s, id: null, ...t })
|
|
218
|
+
});
|
|
219
|
+
if (!o.ok) {
|
|
220
|
+
const i = await o.text();
|
|
221
|
+
throw new Error(
|
|
222
|
+
`Failed to send profile: ${o.status} – ${i}`
|
|
223
|
+
);
|
|
103
224
|
}
|
|
104
225
|
}
|
|
105
226
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
227
|
+
const y = [
|
|
228
|
+
u,
|
|
229
|
+
f,
|
|
230
|
+
m
|
|
109
231
|
];
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
232
|
+
function g(r = {}) {
|
|
233
|
+
const t = new l(r), e = y.map((n) => new n(t)), s = {};
|
|
234
|
+
return e.forEach((n) => {
|
|
235
|
+
[
|
|
236
|
+
...Object.getOwnPropertyNames(n),
|
|
237
|
+
...Object.getOwnPropertyNames(Object.getPrototypeOf(n))
|
|
238
|
+
].forEach((i) => {
|
|
239
|
+
if (i === "constructor") return;
|
|
240
|
+
const p = n[i];
|
|
241
|
+
typeof p == "function" && (s[i] || (s[i] = (...h) => p.apply(n, h)));
|
|
119
242
|
});
|
|
120
|
-
}),
|
|
243
|
+
}), s;
|
|
121
244
|
}
|
|
122
245
|
export {
|
|
123
|
-
|
|
246
|
+
g as createClient
|
|
124
247
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(c,
|
|
1
|
+
(function(c,r){typeof exports=="object"&&typeof module<"u"?r(exports):typeof define=="function"&&define.amd?define(["exports"],r):(c=typeof globalThis<"u"?globalThis:c||self,r(c["sizebay-core-sdk"]={}))})(this,function(c){"use strict";const r={tracker:{production:"https://data-event-service.internalsizebay.com",development:"https://data-event-service-dev.internalsizebay.com"},aiImageService:{production:"https://ai-image-service.internalsizebay.com",development:"https://ai-image-service-dev.internalsizebay.com"},session:{production:"https://vfr-v3-production.sizebay.technology/api/me",development:"https://vfr-v3-staging.sizebay.eu/api/me"}};class u{constructor(e){const t=e.env||"development";this.serviceOverrides=e.services||{},this.endpoints={};for(const s in r)if(Object.prototype.hasOwnProperty.call(r,s)){const o=r[s][t];if(!o)continue;this.endpoints[s]=o}}getEndpoint(e){const t=this.endpoints[e];if(!t)throw new Error(`Endpoint for service '${e}' is not configured.`);return t}getServiceConfig(e){return this.serviceOverrides[e]||{}}}class l{constructor(e){this.endpoint=e.getEndpoint("tracker")}async track(e,t){const s={eventName:e,...t},n=new URL(`${this.endpoint}/events`);try{const o=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!o.ok){const i=await o.text();throw new Error(`Request error: ${o.status} - ${i}`)}return await o.json()}catch(o){throw o}}}class d extends Error{constructor(e,t){let s;try{const n=JSON.parse(t);s=(n==null?void 0:n.message)||t}catch{s=t}super(s),this.statusCode=e,this.details=t,Object.setPrototypeOf(this,d.prototype)}}async function p(a,e){let t;try{t=await fetch(a,e)}catch(n){throw new d(0,n.message)}const s=await t.text();if(!t.ok)throw new d(t.status,s);return JSON.parse(s)}class f{constructor(e){this.endpoint=e.getEndpoint("aiImageService")}appendQueryParams(e,t){Object.entries(t).forEach(([s,n])=>{n!=null&&e.searchParams.append(s,String(n))})}getSimilarProducts(e){const t=new URL(`${this.endpoint}/recommendations/similar`);return this.appendQueryParams(t,e),p(t.toString(),{method:"GET",headers:{"Content-Type":"application/json"}})}getRecommendedSizeByProducts(e){const t=`${this.endpoint}/recommendations/size-by-products`;return p(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})}getComplementaryProducts(e){const t=new URL(`${this.endpoint}/recommendations/complementary`);return this.appendQueryParams(t,e),p(t.toString(),{method:"GET",headers:{"Content-Type":"application/json"}})}}class m{constructor(e){this.sid=null,this.sessionId=null;const t=e.getEndpoint("session");this.sessionEndpoint=`${t}/`,this.profileEndpoint=`${t}/user/profile`}async getSessionInfo(){if(this.sid&&this.sessionId!==null)return{sid:this.sid,sessionId:this.sessionId};const e=await fetch(this.sessionEndpoint,{credentials:"include"});if(!e.ok){const s=await e.text();throw new Error(`Failed to fetch session info: ${e.status} – ${s}`)}const t=await e.json();return this.sid=t.catalogUser.id,this.sessionId=t.sessionId,{sid:this.sid,sessionId:this.sessionId}}async sendProfile(e,t){const s=t??(await this.getSessionInfo()).sid,n=new URL(this.profileEndpoint);n.searchParams.set("sid",s);const o=await fetch(n.toString(),{credentials:"include",method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userId:s,id:null,...e})});if(!o.ok){const i=await o.text();throw new Error(`Failed to send profile: ${o.status} – ${i}`)}}}const y=[l,f,m];function g(a={}){const e=new u(a),t=y.map(n=>new n(e)),s={};return t.forEach(n=>{[...Object.getOwnPropertyNames(n),...Object.getOwnPropertyNames(Object.getPrototypeOf(n))].forEach(i=>{if(i==="constructor")return;const h=n[i];typeof h=="function"&&(s[i]||(s[i]=(...w)=>h.apply(n,w)))})}),s}c.createClient=g,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})});
|
|
@@ -40,3 +40,34 @@ export interface GetRecommendedSizeByProductsResponse {
|
|
|
40
40
|
permalink: string;
|
|
41
41
|
recommendedSize: string | null;
|
|
42
42
|
}
|
|
43
|
+
export interface ProductComplementaryResponseDto {
|
|
44
|
+
id: string;
|
|
45
|
+
tenantId: number;
|
|
46
|
+
title: string;
|
|
47
|
+
productType: string;
|
|
48
|
+
link: string;
|
|
49
|
+
imageLink: string;
|
|
50
|
+
gender: string;
|
|
51
|
+
availability: string;
|
|
52
|
+
productHash: string;
|
|
53
|
+
price: string;
|
|
54
|
+
itemGroupId: string;
|
|
55
|
+
brand: string;
|
|
56
|
+
color: string;
|
|
57
|
+
gtin: string;
|
|
58
|
+
additionalImageLinks: string[];
|
|
59
|
+
}
|
|
60
|
+
export interface GetComplementaryProductsParams {
|
|
61
|
+
tenantId: string;
|
|
62
|
+
collectionName: string;
|
|
63
|
+
sessionId: string;
|
|
64
|
+
permalink: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ComplementaryPairDto {
|
|
67
|
+
first: ProductComplementaryResponseDto;
|
|
68
|
+
secondary: ProductComplementaryResponseDto;
|
|
69
|
+
}
|
|
70
|
+
export interface GetComplementaryProductsResponse {
|
|
71
|
+
baseProduct: ProductComplementaryResponseDto;
|
|
72
|
+
complementary: ComplementaryPairDto[];
|
|
73
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface SessionProfilePayload {
|
|
2
|
+
name: string;
|
|
3
|
+
skinType: number;
|
|
4
|
+
footShape: string | null;
|
|
5
|
+
gender: string;
|
|
6
|
+
age: string;
|
|
7
|
+
is3dFeel: boolean;
|
|
8
|
+
weight: string;
|
|
9
|
+
height: string;
|
|
10
|
+
measures: {
|
|
11
|
+
insoleLength: number;
|
|
12
|
+
poundWeight: number | null;
|
|
13
|
+
};
|
|
14
|
+
product: string | null;
|
|
15
|
+
isMetric: boolean;
|
|
16
|
+
bodyShapeChest: number;
|
|
17
|
+
bodyShapeHip: number;
|
|
18
|
+
bodyShapeWaist: number;
|
|
19
|
+
}
|
|
20
|
+
export interface SessionContext {
|
|
21
|
+
sid: string;
|
|
22
|
+
sessionId: number;
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sizebay-core-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A SDK designed for integrating multiple services (such as event tracking, AI services, etc.) into your application.",
|
|
5
5
|
"main": "dist/sizebay-core-sdk.umd.js",
|
|
6
6
|
"module": "dist/sizebay-core-sdk.es.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"prepublishOnly": "npm run build",
|
|
16
16
|
"test": "jest",
|
|
17
17
|
"test:ci": "jest --ci",
|
|
18
|
-
"test:dev": "
|
|
18
|
+
"test:dev": "tsx test/example.ts"
|
|
19
19
|
},
|
|
20
20
|
"author": "sizebay",
|
|
21
21
|
"license": "ISC",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"ts-jest": "^29.2.5",
|
|
41
41
|
"ts-node": "^10.9.2",
|
|
42
42
|
"tsconfig-paths": "^4.2.0",
|
|
43
|
+
"tsx": "^4.19.3",
|
|
43
44
|
"typescript": "^5.7.3",
|
|
44
45
|
"vite": "^6.0.11",
|
|
45
46
|
"vite-plugin-dts": "^4.5.0",
|