@fluentcommerce/fc-connect-sdk 0.1.48 → 0.1.52

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +506 -379
  2. package/README.md +343 -0
  3. package/dist/cjs/clients/fluent-client.js +110 -14
  4. package/dist/cjs/data-sources/s3-data-source.js +1 -1
  5. package/dist/cjs/data-sources/sftp-data-source.js +1 -1
  6. package/dist/cjs/index.d.ts +1 -1
  7. package/dist/cjs/services/extraction/extraction-orchestrator.d.ts +4 -1
  8. package/dist/cjs/services/extraction/extraction-orchestrator.js +84 -11
  9. package/dist/cjs/types/index.d.ts +79 -10
  10. package/dist/cjs/versori/fluent-versori-client.d.ts +4 -1
  11. package/dist/cjs/versori/fluent-versori-client.js +131 -13
  12. package/dist/esm/clients/fluent-client.js +110 -14
  13. package/dist/esm/data-sources/s3-data-source.js +1 -1
  14. package/dist/esm/data-sources/sftp-data-source.js +1 -1
  15. package/dist/esm/index.d.ts +1 -1
  16. package/dist/esm/services/extraction/extraction-orchestrator.d.ts +4 -1
  17. package/dist/esm/services/extraction/extraction-orchestrator.js +84 -11
  18. package/dist/esm/types/index.d.ts +79 -10
  19. package/dist/esm/versori/fluent-versori-client.d.ts +4 -1
  20. package/dist/esm/versori/fluent-versori-client.js +131 -13
  21. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/dist/tsconfig.types.tsbuildinfo +1 -1
  24. package/dist/types/index.d.ts +1 -1
  25. package/dist/types/services/extraction/extraction-orchestrator.d.ts +4 -1
  26. package/dist/types/types/index.d.ts +79 -10
  27. package/dist/types/versori/fluent-versori-client.d.ts +4 -1
  28. package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +478 -18
  29. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +83 -0
  30. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +52 -0
  31. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -0
  32. package/docs/02-CORE-GUIDES/api-reference/readme.md +1 -1
  33. package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +68 -4
  34. package/docs/02-CORE-GUIDES/mapping/modules/mapping-01-foundations.md +450 -448
  35. package/docs/02-CORE-GUIDES/mapping/modules/mapping-02-quick-start.md +476 -474
  36. package/docs/02-CORE-GUIDES/mapping/modules/mapping-03-schema-validation.md +464 -462
  37. package/docs/02-CORE-GUIDES/mapping/modules/mapping-05-advanced-patterns.md +1366 -1364
  38. package/docs/readme.md +245 -245
  39. package/package.json +17 -6
  40. package/docs/versori-apis/ACTIVATIONS-AND-VARIABLES-GUIDE.md +0 -60
  41. package/docs/versori-apis/JWT-GENERATION-GUIDE.md +0 -94
  42. package/docs/versori-apis/QUICK-WORKFLOW.md +0 -293
  43. package/docs/versori-apis/README.md +0 -73
  44. package/docs/versori-apis/VERSORI-PLATFORM-ARCHITECTURE.md +0 -880
  45. package/docs/versori-apis/Versori-Platform-API.postman_collection.json +0 -2925
  46. package/docs/versori-apis/Versori-Platform-API.postman_environment.example.json +0 -62
  47. package/docs/versori-apis/Versori-Platform-API.postman_environment.json +0 -178
@@ -80,8 +80,9 @@ export class FluentVersoriClient {
80
80
  });
81
81
  const paginationVars = detectPaginationVariables(payload.query);
82
82
  const paginationEnabled = !isMutation && (payload.pagination?.enabled ?? paginationVars.hasPagination);
83
+ const errorHandling = payload.errorHandling ?? 'throw';
83
84
  if (!paginationEnabled) {
84
- return this.executeSinglePage(payload);
85
+ return this.executeSinglePage(payload, errorHandling);
85
86
  }
86
87
  const direction = payload.pagination?.direction ??
87
88
  paginationVars.direction ??
@@ -97,10 +98,11 @@ export class FluentVersoriClient {
97
98
  abortSignal: payload.pagination?.abortSignal,
98
99
  onProgress: payload.pagination?.onProgress ?? (() => { }),
99
100
  onWarning: payload.pagination?.onWarning ?? ((msg) => this.ctx.log?.warn?.(msg)),
101
+ errorHandling,
100
102
  };
101
103
  return this.executeWithAutoPagination(payload, paginationVars, config);
102
104
  }
103
- async executeSinglePage(payload) {
105
+ async executeSinglePage(payload, errorHandling = 'throw') {
104
106
  const operationName = payload.operationName || 'unnamed';
105
107
  try {
106
108
  const apiPayload = {
@@ -139,6 +141,18 @@ export class FluentVersoriClient {
139
141
  throw new Error('Failed to parse GraphQL response');
140
142
  }
141
143
  if (parsedResponse.errors && parsedResponse.errors.length > 0) {
144
+ if (errorHandling === 'partial') {
145
+ this.ctx.log.warn(`[fc-connect-sdk:graphql] GraphQL operation "${operationName}" returned partial data with ${parsedResponse.errors.length} error(s)`, {
146
+ operationName,
147
+ errors: parsedResponse.errors,
148
+ errorCount: parsedResponse.errors.length,
149
+ hasData: !!parsedResponse.data,
150
+ });
151
+ return {
152
+ ...parsedResponse,
153
+ hasPartialData: true,
154
+ };
155
+ }
142
156
  this.ctx.log.error(`[fc-connect-sdk:graphql] GraphQL operation "${operationName}" returned ${parsedResponse.errors.length} error(s)`, {
143
157
  operationName,
144
158
  errors: parsedResponse.errors,
@@ -175,6 +189,7 @@ export class FluentVersoriClient {
175
189
  let currentVariables = { ...(payload.variables || {}) };
176
190
  let lastCursor = null;
177
191
  let emptyPageCount = 0;
192
+ const accumulatedErrors = [];
178
193
  while (true) {
179
194
  if (config.abortSignal?.aborted) {
180
195
  truncated = true;
@@ -197,17 +212,29 @@ export class FluentVersoriClient {
197
212
  const response = await this.executeSinglePage({
198
213
  query: payload.query,
199
214
  variables: currentVariables,
200
- });
215
+ }, 'partial');
201
216
  lastExtensions = response.extensions;
202
217
  if (response.errors && response.errors.length > 0) {
203
- this.ctx.log.error(`[fc-connect-sdk:pagination] GraphQL errors during pagination (page ${pageNumber + 1}, ${totalRecords} records fetched so far)`, {
204
- errors: response.errors,
205
- pagesCompleted: pageNumber,
206
- totalRecordsBeforeError: totalRecords,
207
- errorCount: response.errors.length,
208
- });
209
- const firstError = response.errors[0];
210
- throw new GraphQLExecutionError(`GraphQL error during pagination (page ${pageNumber}): ${firstError.message || 'Unknown error'}`, response.errors, payload.query, currentVariables);
218
+ if (config.errorHandling === 'partial') {
219
+ this.ctx.log.warn(`[fc-connect-sdk:pagination] Page ${pageNumber + 1} returned partial data with ${response.errors.length} error(s)`, {
220
+ errors: response.errors,
221
+ pagesCompleted: pageNumber,
222
+ totalRecordsSoFar: totalRecords,
223
+ errorCount: response.errors.length,
224
+ hasData: !!response.data,
225
+ });
226
+ accumulatedErrors.push(...response.errors);
227
+ }
228
+ else {
229
+ this.ctx.log.error(`[fc-connect-sdk:pagination] GraphQL errors during pagination (page ${pageNumber + 1}, ${totalRecords} records fetched so far)`, {
230
+ errors: response.errors,
231
+ pagesCompleted: pageNumber,
232
+ totalRecordsBeforeError: totalRecords,
233
+ errorCount: response.errors.length,
234
+ });
235
+ const firstError = response.errors[0];
236
+ throw new GraphQLExecutionError(`GraphQL error during pagination (page ${pageNumber}): ${firstError.message || 'Unknown error'}`, response.errors, payload.query, currentVariables);
237
+ }
211
238
  }
212
239
  pageNumber++;
213
240
  const connection = extractConnection(response.data, config.connectionPath);
@@ -310,14 +337,17 @@ export class FluentVersoriClient {
310
337
  await new Promise(resolve => setTimeout(resolve, config.delayMs));
311
338
  }
312
339
  }
313
- this.ctx.log?.info?.(`[fc-connect-sdk:pagination] Pagination complete (${pageNumber} pages, ${totalRecords} records${truncated ? `, truncated: ${truncationReason}` : ''})`, {
340
+ const hasPartialErrors = accumulatedErrors.length > 0;
341
+ this.ctx.log?.info?.(`[fc-connect-sdk:pagination] Pagination complete (${pageNumber} pages, ${totalRecords} records${truncated ? `, truncated: ${truncationReason}` : ''}${hasPartialErrors ? `, ${accumulatedErrors.length} errors` : ''})`, {
314
342
  totalPages: pageNumber,
315
343
  totalRecords,
316
344
  truncated,
317
345
  truncationReason,
318
346
  duration: Date.now() - startTime,
347
+ hasPartialErrors,
348
+ errorCount: accumulatedErrors.length,
319
349
  });
320
- return {
350
+ const response = {
321
351
  data: allData,
322
352
  extensions: {
323
353
  ...(lastExtensions || {}),
@@ -330,6 +360,11 @@ export class FluentVersoriClient {
330
360
  },
331
361
  },
332
362
  };
363
+ if (hasPartialErrors) {
364
+ response.errors = accumulatedErrors;
365
+ response.hasPartialData = true;
366
+ }
367
+ return response;
333
368
  }
334
369
  async sendEvent(event, mode = 'async') {
335
370
  this.ctx.log.info(`[fc-connect-sdk:event] Sending event "${event.name}" (${event.entityType}:${event.entityRef}, mode: ${mode})`, {
@@ -389,6 +424,79 @@ export class FluentVersoriClient {
389
424
  return { success: true, statusCode: response.status, message: text };
390
425
  }
391
426
  }
427
+ async getEvents(params = {}) {
428
+ const query = this.buildEventQueryString(params);
429
+ const endpoint = query ? `/api/v4.1/event?${query}` : '/api/v4.1/event';
430
+ this.ctx.log.info('[fc-connect-sdk:event] Searching event logs', {
431
+ endpoint,
432
+ filterCount: Object.keys(params).length,
433
+ hasDateRange: !!(params.from || params.to),
434
+ entityType: params['context.entityType'],
435
+ rootEntityType: params['context.rootEntityType'],
436
+ eventStatus: params.eventStatus,
437
+ });
438
+ const response = await this.fetchWithRetry(endpoint, {
439
+ method: 'GET',
440
+ headers: {
441
+ 'Content-Type': 'application/json',
442
+ },
443
+ });
444
+ const text = response.text;
445
+ if (!response.ok) {
446
+ this.ctx.log.error('[fc-connect-sdk:event] Failed to search events', {
447
+ status: response.status,
448
+ body: text,
449
+ });
450
+ this.throwApiError('Get events failed', response.status, text);
451
+ }
452
+ try {
453
+ const result = JSON.parse(text);
454
+ this.ctx.log.info('[fc-connect-sdk:event] Event search completed', {
455
+ resultCount: result.count,
456
+ hasMore: result.hasMore,
457
+ start: result.start,
458
+ });
459
+ return result;
460
+ }
461
+ catch {
462
+ throw new FluentAPIError('Failed to parse event log response', response.status, text);
463
+ }
464
+ }
465
+ async getEventById(eventId) {
466
+ if (!eventId) {
467
+ throw new FluentValidationError('eventId is required');
468
+ }
469
+ this.ctx.log.info(`[fc-connect-sdk:event] Getting event by ID: ${eventId}`, { eventId });
470
+ const response = await this.fetchWithRetry(`/api/v4.1/event/${eventId}`, {
471
+ method: 'GET',
472
+ headers: {
473
+ 'Content-Type': 'application/json',
474
+ },
475
+ });
476
+ const text = response.text;
477
+ if (!response.ok) {
478
+ this.ctx.log.error(`[fc-connect-sdk:event] Failed to get event ${eventId}`, {
479
+ eventId,
480
+ status: response.status,
481
+ body: text,
482
+ });
483
+ this.throwApiError(`Get event ${eventId} failed`, response.status, text);
484
+ }
485
+ try {
486
+ const event = JSON.parse(text);
487
+ this.ctx.log.info(`[fc-connect-sdk:event] Event retrieved: ${event.name}`, {
488
+ eventId: event.id,
489
+ name: event.name,
490
+ type: event.type,
491
+ eventStatus: event.eventStatus,
492
+ entityType: event.context?.entityType,
493
+ });
494
+ return event;
495
+ }
496
+ catch {
497
+ throw new FluentAPIError('Failed to parse event response', response.status, text);
498
+ }
499
+ }
392
500
  async createJob(payload) {
393
501
  this.ctx.log.info(`[fc-connect-sdk:job] Creating job "${payload.name}"`, {
394
502
  name: payload.name,
@@ -536,6 +644,16 @@ export class FluentVersoriClient {
536
644
  const result = await this.graphql(payload);
537
645
  return result.data;
538
646
  }
647
+ buildEventQueryString(params) {
648
+ const searchParams = new URLSearchParams();
649
+ for (const [key, value] of Object.entries(params)) {
650
+ if (value === undefined || value === null || value === '') {
651
+ continue;
652
+ }
653
+ searchParams.append(key, String(value));
654
+ }
655
+ return searchParams.toString();
656
+ }
539
657
  throwApiError(message, status, responseText) {
540
658
  if (status === 401) {
541
659
  throw new AuthenticationError(`${message}: ${responseText}`, { response: responseText });