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 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
+ ```
@@ -0,0 +1,6 @@
1
+ export declare class ApiError extends Error {
2
+ statusCode: number;
3
+ details: string;
4
+ constructor(statusCode: number, details: string);
5
+ }
6
+ export declare function safeFetchJson<T>(url: string, init: RequestInit): Promise<T>;
@@ -1,8 +1,10 @@
1
1
  import { Config } from '../config';
2
- import { GetRecommendedSizeByProductsParams, GetRecommendedSizeByProductsResponse, GetSimilarProductsParams, GetSimilarProductsResponse } from '../types/ai-image-service.types';
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
  }
@@ -1,3 +1,4 @@
1
1
  import { Tracker } from './tracker';
2
2
  import { AIImageService } from './ai-image-service';
3
- export declare const moduleClasses: (typeof Tracker | typeof AIImageService)[];
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 a {
13
- constructor(r) {
14
- const n = r.env || "development";
15
- this.serviceOverrides = r.services || {}, this.endpoints = {};
16
- for (const t in c)
17
- if (Object.prototype.hasOwnProperty.call(c, t)) {
18
- const o = c[t][n];
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[t] = o;
25
+ this.endpoints[s] = o;
22
26
  }
23
27
  }
24
- getEndpoint(r) {
25
- const n = this.endpoints[r];
26
- if (!n)
28
+ getEndpoint(t) {
29
+ const e = this.endpoints[t];
30
+ if (!e)
27
31
  throw new Error(
28
- `Endpoint for service '${r}' is not configured.`
32
+ `Endpoint for service '${t}' is not configured.`
29
33
  );
30
- return n;
34
+ return e;
31
35
  }
32
- getServiceConfig(r) {
33
- return this.serviceOverrides[r] || {};
36
+ getServiceConfig(t) {
37
+ return this.serviceOverrides[t] || {};
34
38
  }
35
39
  }
36
- class p {
37
- constructor(r) {
38
- this.endpoint = r.getEndpoint("tracker");
39
- }
40
- async track(r, n) {
41
- const t = {
42
- eventName: r,
43
- ...n
44
- }, e = new URL(`${this.endpoint}/events`);
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(e, {
50
+ const o = await fetch(n, {
47
51
  method: "POST",
48
52
  headers: { "Content-Type": "application/json" },
49
- body: JSON.stringify(t)
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 d {
62
- constructor(r) {
63
- this.endpoint = r.getEndpoint("aiImageService");
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 t = await fetch(n.toString(), {
72
- method: "GET",
73
- headers: {
74
- "Content-Type": "application/json"
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
- async getRecommendedSizeByProducts(r) {
87
- const n = new URL(`${this.endpoint}/recommendations/size-by-products`);
88
- try {
89
- const t = await fetch(n.toString(), {
90
- method: "POST",
91
- headers: {
92
- "Content-Type": "application/json"
93
- },
94
- body: JSON.stringify(r)
95
- });
96
- if (!t.ok) {
97
- const e = await t.text();
98
- throw new Error(`Request error: ${t.status} - ${e}`);
99
- }
100
- return await t.json();
101
- } catch (t) {
102
- throw new Error(`Error fetching recommended product size: ${t.message}`);
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 h = [
107
- p,
108
- d
227
+ const y = [
228
+ u,
229
+ f,
230
+ m
109
231
  ];
110
- function f(s = {}) {
111
- const r = new a(s), n = h.map((e) => new e(r)), t = {};
112
- return n.forEach((e) => {
113
- Object.getOwnPropertyNames(e).forEach((i) => {
114
- typeof e[i] == "function" && (t[i] = e[i].bind(e));
115
- });
116
- const o = Object.getPrototypeOf(e);
117
- Object.getOwnPropertyNames(o).forEach((i) => {
118
- i !== "constructor" && typeof e[i] == "function" && (t[i] = e[i].bind(e));
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
- }), t;
243
+ }), s;
121
244
  }
122
245
  export {
123
- f as createClient
246
+ g as createClient
124
247
  };
@@ -1 +1 @@
1
- (function(c,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(c=typeof globalThis<"u"?globalThis:c||self,s(c["sizebay-core-sdk"]={}))})(this,function(c){"use strict";const s={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"}};class d{constructor(r){const n=r.env||"development";this.serviceOverrides=r.services||{},this.endpoints={};for(const e in s)if(Object.prototype.hasOwnProperty.call(s,e)){const o=s[e][n];if(!o)continue;this.endpoints[e]=o}}getEndpoint(r){const n=this.endpoints[r];if(!n)throw new Error(`Endpoint for service '${r}' is not configured.`);return n}getServiceConfig(r){return this.serviceOverrides[r]||{}}}class p{constructor(r){this.endpoint=r.getEndpoint("tracker")}async track(r,n){const e={eventName:r,...n},t=new URL(`${this.endpoint}/events`);try{const o=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});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 f{constructor(r){this.endpoint=r.getEndpoint("aiImageService")}async getSimilarProducts(r){const n=new URL(`${this.endpoint}/recommendations/similar`);Object.entries(r).forEach(([e,t])=>{t!==void 0&&n.searchParams.append(e,String(t))});try{const e=await fetch(n.toString(),{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.text();throw new Error(`Request error: ${e.status} - ${t}`)}return await e.json()}catch(e){throw new Error(`Error fetching similar products: ${e.message}`)}}async getRecommendedSizeByProducts(r){const n=new URL(`${this.endpoint}/recommendations/size-by-products`);try{const e=await fetch(n.toString(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});if(!e.ok){const t=await e.text();throw new Error(`Request error: ${e.status} - ${t}`)}return await e.json()}catch(e){throw new Error(`Error fetching recommended product size: ${e.message}`)}}}const h=[p,f];function u(a={}){const r=new d(a),n=h.map(t=>new t(r)),e={};return n.forEach(t=>{Object.getOwnPropertyNames(t).forEach(i=>{typeof t[i]=="function"&&(e[i]=t[i].bind(t))});const o=Object.getPrototypeOf(t);Object.getOwnPropertyNames(o).forEach(i=>{i!=="constructor"&&typeof t[i]=="function"&&(e[i]=t[i].bind(t))})}),e}c.createClient=u,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})});
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.1.4",
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": "ts-node -r tsconfig-paths/register test/example.ts"
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",