@fluentcommerce/fc-connect-sdk 0.1.52 → 0.1.54

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 (34) hide show
  1. package/CHANGELOG.md +530 -506
  2. package/README.md +52 -3
  3. package/dist/cjs/auth/index.d.ts +3 -0
  4. package/dist/cjs/auth/index.js +13 -0
  5. package/dist/cjs/auth/profile-loader.d.ts +18 -0
  6. package/dist/cjs/auth/profile-loader.js +208 -0
  7. package/dist/cjs/client-factory.d.ts +4 -0
  8. package/dist/cjs/client-factory.js +10 -0
  9. package/dist/cjs/index.d.ts +3 -1
  10. package/dist/cjs/index.js +8 -2
  11. package/dist/cjs/types/index.d.ts +1 -1
  12. package/dist/esm/auth/index.d.ts +3 -0
  13. package/dist/esm/auth/index.js +2 -0
  14. package/dist/esm/auth/profile-loader.d.ts +18 -0
  15. package/dist/esm/auth/profile-loader.js +169 -0
  16. package/dist/esm/client-factory.d.ts +4 -0
  17. package/dist/esm/client-factory.js +9 -0
  18. package/dist/esm/index.d.ts +3 -1
  19. package/dist/esm/index.js +2 -1
  20. package/dist/esm/types/index.d.ts +1 -1
  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/auth/index.d.ts +3 -0
  25. package/dist/types/auth/profile-loader.d.ts +18 -0
  26. package/dist/types/client-factory.d.ts +4 -0
  27. package/dist/types/index.d.ts +3 -1
  28. package/dist/types/types/index.d.ts +1 -1
  29. package/docs/02-CORE-GUIDES/api-reference/cli-profile-integration.md +377 -0
  30. package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +24 -15
  31. package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +38 -0
  32. package/docs/02-CORE-GUIDES/webhook-validation/modules/webhook-validation-09-rubix-event-vs-http-call.md +1 -1
  33. package/docs/03-PATTERN-GUIDES/multiple-connections/modules/multiple-connections-02-quick-start.md +1 -1
  34. package/package.json +1 -1
package/README.md CHANGED
@@ -31,6 +31,8 @@ TypeScript SDK for building **Fluent Commerce** integrations across Node.js, Den
31
31
  - [More Examples](#-more-quick-examples) - Common patterns
32
32
  - [Core Services](#core-services) - API reference
33
33
  - [CLI Tools](#cli-tooling) - Command-line utilities
34
+ - [Standalone Usage](#-standalone-usage-outside-versori) - Node.js/Deno app integration
35
+ - [CLI Profile Integration](#fluent-cli-profile-integration-no-hardcoded-credentials) - Reuse Fluent CLI profiles
34
36
  - [Authentication & Webhooks](#authentication-webhooks) - Security
35
37
  - [Security & Compliance](#security--compliance) - Security audits, SOC 2, vulnerability management 🆕
36
38
  - [Common Pitfalls](#common-pitfalls-and-fixes) - Troubleshooting
@@ -1309,7 +1311,17 @@ if (logs.results[0]?.id) {
1309
1311
 
1310
1312
  #### Query Parameters
1311
1313
 
1312
- All parameters are optional. Context filters use flat dot-notation keys:
1314
+ All parameters are optional. Context filters use **dot-notation keys** which require quotes in JavaScript/TypeScript:
1315
+
1316
+ ```typescript
1317
+ // ⚠️ Dot-notation keys MUST be quoted (JavaScript syntax requirement)
1318
+ await client.getEvents({
1319
+ 'context.rootEntityType': 'ORDER', // ✅ Quotes required (has dot)
1320
+ 'context.entityRef': 'ORD-123', // ✅ Quotes required (has dot)
1321
+ eventType: 'ORCHESTRATION_AUDIT', // ✅ No quotes needed (simple key)
1322
+ count: 100, // ✅ No quotes needed (simple key)
1323
+ });
1324
+ ```
1313
1325
 
1314
1326
  | Parameter | Type | Description | Example |
1315
1327
  |-----------|------|-------------|---------|
@@ -1325,7 +1337,7 @@ All parameters are optional. Context filters use flat dot-notation keys:
1325
1337
  | `category` | string | Category filter | `'ruleSet'`, `'ACTION'` |
1326
1338
  | `from` | string | Start date (UTC ISO 8601) | `'2026-02-01T00:00:00.000Z'` |
1327
1339
  | `to` | string | End date (UTC ISO 8601) | `'2026-02-15T23:59:59.999Z'` |
1328
- | `retailerId` | string/number | Retailer filter (falls back to client config) | `'5'` |
1340
+ | `retailerId` | string/number | Retailer filter (pass explicitly; no auto-fallback) | `'5'` |
1329
1341
  | `start` | number | Pagination offset (default: `0`) | `0` |
1330
1342
  | `count` | number | Results per page (default: `100`, max: `5000`) | `1000` |
1331
1343
 
@@ -1418,7 +1430,7 @@ const orderEvents = await client.getEvents({
1418
1430
  | **Time range (to)** | 1 month forward max | API rejects future dates beyond ~1 month |
1419
1431
  | **Default time window** | Past 30 days | Applied when `from` is omitted |
1420
1432
  | **Date format** | UTC ISO 8601 | `YYYY-MM-DDTHH:mm:ss.SSSZ` |
1421
- | **retailerId fallback** | Client config | If not in params, uses `client.config.retailerId` |
1433
+ | **retailerId** | Pass explicitly | Unlike `sendEvent()`, no auto-fallback to client config — pass in params if needed |
1422
1434
  | **attributes field** | Nullable | Can be `null` (not empty array) — always check before iterating |
1423
1435
  | **name field** | Nullable | Can be `null` for some system events |
1424
1436
  | **GraphQL** | Not available | Event queries use REST only — no GraphQL `events` root field exists |
@@ -1658,6 +1670,43 @@ console.log('Status:', response.status);
1658
1670
  console.log('Data:', response.data);
1659
1671
  ```
1660
1672
 
1673
+ ### Fluent CLI Profile Integration (No Hardcoded Credentials)
1674
+
1675
+ If you already use the Fluent CLI (`fluent profile create`, `fluent profile switch`), you can reuse the same profile files directly in the SDK.
1676
+
1677
+ ```typescript
1678
+ import { createClientFromProfile, listFluentProfiles } from '@fluentcommerce/fc-connect-sdk';
1679
+
1680
+ // Discover available local Fluent CLI profiles (~/.fluentcommerce/*)
1681
+ const profiles = listFluentProfiles();
1682
+ // Example: ['PROFILE_ALPHA', 'PROFILE_BETA']
1683
+
1684
+ // Create client from profile.json + user.*.json
1685
+ const client = await createClientFromProfile('PROFILE_ALPHA');
1686
+
1687
+ // Multi-retailer profile: pass retailer ref to load retailerId + retailer user override
1688
+ const retailerClient = await createClientFromProfile('PROFILE_BETA', {
1689
+ retailer: 'RETAILER_ALPHA',
1690
+ validateConnection: true, // Optional fail-fast auth check
1691
+ });
1692
+
1693
+ const events = await retailerClient.getEvents({
1694
+ eventType: 'ORCHESTRATION_AUDIT',
1695
+ 'context.rootEntityType': 'ORDER',
1696
+ });
1697
+ ```
1698
+
1699
+ **Behavior and limitations:**
1700
+
1701
+ - Uses Fluent CLI profile files from `~/.fluentcommerce/<PROFILE>/`:
1702
+ - `profile.json` (base URL, client ID/secret, default user)
1703
+ - `retailer.<REF>.json` (optional retailer ID + retailer-specific user override)
1704
+ - `user.<name>.json` (username/password)
1705
+ - For multi-retailer setups, pass `retailer` to ensure the correct retailer-specific user credentials are loaded.
1706
+ - Throws clear errors when profile directories/files are missing or malformed.
1707
+ - Supports profile directory overrides via `FLUENT_PROFILE_DIR` or `options.profileDir`.
1708
+ - Intended for standalone Node.js/Deno usage; in Versori workflows use `createClient(ctx)` instead.
1709
+
1661
1710
  ### What's Handled Automatically
1662
1711
 
1663
1712
  - ✅ **OAuth2 Authentication** - Token fetched and cached automatically
@@ -0,0 +1,3 @@
1
+ export { loadFluentProfile, listFluentProfiles, getFluentProfileInfo, resolveProfileBaseDir, } from './profile-loader';
2
+ export type { FluentProfileOptions, FluentProfileInfo } from './profile-loader';
3
+ export { OAuth2AuthProvider, BearerTokenAuthProvider, AuthManager, VersoriConnectionAdapter, type VersoriConnection, } from './auth-provider';
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VersoriConnectionAdapter = exports.AuthManager = exports.BearerTokenAuthProvider = exports.OAuth2AuthProvider = exports.resolveProfileBaseDir = exports.getFluentProfileInfo = exports.listFluentProfiles = exports.loadFluentProfile = void 0;
4
+ var profile_loader_1 = require("./profile-loader");
5
+ Object.defineProperty(exports, "loadFluentProfile", { enumerable: true, get: function () { return profile_loader_1.loadFluentProfile; } });
6
+ Object.defineProperty(exports, "listFluentProfiles", { enumerable: true, get: function () { return profile_loader_1.listFluentProfiles; } });
7
+ Object.defineProperty(exports, "getFluentProfileInfo", { enumerable: true, get: function () { return profile_loader_1.getFluentProfileInfo; } });
8
+ Object.defineProperty(exports, "resolveProfileBaseDir", { enumerable: true, get: function () { return profile_loader_1.resolveProfileBaseDir; } });
9
+ var auth_provider_1 = require("./auth-provider");
10
+ Object.defineProperty(exports, "OAuth2AuthProvider", { enumerable: true, get: function () { return auth_provider_1.OAuth2AuthProvider; } });
11
+ Object.defineProperty(exports, "BearerTokenAuthProvider", { enumerable: true, get: function () { return auth_provider_1.BearerTokenAuthProvider; } });
12
+ Object.defineProperty(exports, "AuthManager", { enumerable: true, get: function () { return auth_provider_1.AuthManager; } });
13
+ Object.defineProperty(exports, "VersoriConnectionAdapter", { enumerable: true, get: function () { return auth_provider_1.VersoriConnectionAdapter; } });
@@ -0,0 +1,18 @@
1
+ import { FluentClientConfig } from '../types';
2
+ export interface FluentProfileOptions {
3
+ profileDir?: string;
4
+ retailer?: string;
5
+ timeout?: number;
6
+ retryConfig?: FluentClientConfig['retryConfig'];
7
+ }
8
+ export interface FluentProfileInfo {
9
+ name: string;
10
+ baseUrl: string;
11
+ accountId: string;
12
+ username: string;
13
+ retailers: string[];
14
+ }
15
+ export declare function resolveProfileBaseDir(options?: FluentProfileOptions): string;
16
+ export declare function loadFluentProfile(profileName: string, options?: FluentProfileOptions): FluentClientConfig;
17
+ export declare function listFluentProfiles(options?: FluentProfileOptions): string[];
18
+ export declare function getFluentProfileInfo(profileName: string, options?: FluentProfileOptions): FluentProfileInfo | null;
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolveProfileBaseDir = resolveProfileBaseDir;
37
+ exports.loadFluentProfile = loadFluentProfile;
38
+ exports.listFluentProfiles = listFluentProfiles;
39
+ exports.getFluentProfileInfo = getFluentProfileInfo;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const os = __importStar(require("os"));
43
+ function resolveProfileBaseDir(options) {
44
+ if (options?.profileDir) {
45
+ return options.profileDir;
46
+ }
47
+ const envDir = process.env.FLUENT_PROFILE_DIR;
48
+ if (envDir) {
49
+ return envDir;
50
+ }
51
+ return path.join(os.homedir(), '.fluentcommerce');
52
+ }
53
+ function loadFluentProfile(profileName, options) {
54
+ if (!profileName || typeof profileName !== 'string') {
55
+ throw new Error('Profile name is required');
56
+ }
57
+ const baseDir = resolveProfileBaseDir(options);
58
+ const profileDir = path.join(baseDir, profileName);
59
+ if (!fs.existsSync(profileDir)) {
60
+ const available = listFluentProfiles(options);
61
+ throw new Error(`Fluent CLI profile '${profileName}' not found at ${profileDir}. ` +
62
+ (available.length > 0
63
+ ? `Available profiles: ${available.join(', ')}`
64
+ : `No profiles found in ${baseDir}. Create one with: fluent profile create ${profileName}`));
65
+ }
66
+ const profileJsonPath = path.join(profileDir, 'profile.json');
67
+ if (!fs.existsSync(profileJsonPath)) {
68
+ throw new Error(`profile.json not found in ${profileDir}. ` +
69
+ `This directory may not be a valid Fluent CLI profile.`);
70
+ }
71
+ let profile;
72
+ try {
73
+ const raw = fs.readFileSync(profileJsonPath, 'utf-8');
74
+ profile = JSON.parse(raw);
75
+ }
76
+ catch (err) {
77
+ throw new Error(`Failed to read ${profileJsonPath}: ${err instanceof Error ? err.message : String(err)}`);
78
+ }
79
+ if (!profile.baseUrl) {
80
+ throw new Error(`Profile '${profileName}' is missing required field 'baseUrl' in profile.json`);
81
+ }
82
+ if (!profile.id) {
83
+ throw new Error(`Profile '${profileName}' is missing required field 'id' in profile.json`);
84
+ }
85
+ if (!profile.clientSecret) {
86
+ throw new Error(`Profile '${profileName}' is missing required field 'clientSecret' in profile.json`);
87
+ }
88
+ if (!profile.user) {
89
+ throw new Error(`Profile '${profileName}' is missing required field 'user' in profile.json`);
90
+ }
91
+ let retailerId;
92
+ let resolvedUser = profile.user;
93
+ if (options?.retailer) {
94
+ const retailerJsonPath = path.join(profileDir, `retailer.${options.retailer}.json`);
95
+ if (!fs.existsSync(retailerJsonPath)) {
96
+ const availableRetailers = listProfileRetailers(profileDir);
97
+ throw new Error(`Retailer '${options.retailer}' not found for profile '${profileName}' at ${retailerJsonPath}. ` +
98
+ (availableRetailers.length > 0
99
+ ? `Available retailers: ${availableRetailers.join(', ')}`
100
+ : `No retailer files found in ${profileDir}.`));
101
+ }
102
+ let retailer;
103
+ try {
104
+ const raw = fs.readFileSync(retailerJsonPath, 'utf-8');
105
+ retailer = JSON.parse(raw);
106
+ }
107
+ catch (err) {
108
+ throw new Error(`Failed to read ${retailerJsonPath}: ${err instanceof Error ? err.message : String(err)}`);
109
+ }
110
+ retailerId = retailer.id;
111
+ if (retailer.user) {
112
+ resolvedUser = retailer.user;
113
+ }
114
+ }
115
+ const userJsonPath = path.join(profileDir, `user.${resolvedUser}.json`);
116
+ if (!fs.existsSync(userJsonPath)) {
117
+ throw new Error(`User credentials file not found: ${userJsonPath}. ` +
118
+ `Expected file for user '${resolvedUser}'` +
119
+ (options?.retailer
120
+ ? ` (set by retailer '${options.retailer}').`
121
+ : ` (set in profile.json).`));
122
+ }
123
+ let userCreds;
124
+ try {
125
+ const raw = fs.readFileSync(userJsonPath, 'utf-8');
126
+ userCreds = JSON.parse(raw);
127
+ }
128
+ catch (err) {
129
+ throw new Error(`Failed to read ${userJsonPath}: ${err instanceof Error ? err.message : String(err)}`);
130
+ }
131
+ if (!userCreds.username || !userCreds.password) {
132
+ throw new Error(`User credentials file ${userJsonPath} is missing 'username' or 'password' field`);
133
+ }
134
+ const config = {
135
+ baseUrl: profile.baseUrl,
136
+ clientId: profile.id,
137
+ clientSecret: profile.clientSecret,
138
+ username: userCreds.username,
139
+ password: userCreds.password,
140
+ };
141
+ if (retailerId) {
142
+ config.retailerId = retailerId;
143
+ }
144
+ if (options?.timeout) {
145
+ config.timeout = options.timeout;
146
+ }
147
+ if (options?.retryConfig) {
148
+ config.retryConfig = options.retryConfig;
149
+ }
150
+ return config;
151
+ }
152
+ function listFluentProfiles(options) {
153
+ const baseDir = resolveProfileBaseDir(options);
154
+ if (!fs.existsSync(baseDir)) {
155
+ return [];
156
+ }
157
+ try {
158
+ const entries = fs.readdirSync(baseDir, { withFileTypes: true });
159
+ return entries
160
+ .filter(entry => {
161
+ if (!entry.isDirectory())
162
+ return false;
163
+ if (entry.name.startsWith('.'))
164
+ return false;
165
+ const profileJson = path.join(baseDir, entry.name, 'profile.json');
166
+ return fs.existsSync(profileJson);
167
+ })
168
+ .map(entry => entry.name)
169
+ .sort();
170
+ }
171
+ catch {
172
+ return [];
173
+ }
174
+ }
175
+ function getFluentProfileInfo(profileName, options) {
176
+ const baseDir = resolveProfileBaseDir(options);
177
+ const profileDirPath = path.join(baseDir, profileName);
178
+ const profileJsonPath = path.join(profileDirPath, 'profile.json');
179
+ if (!fs.existsSync(profileJsonPath)) {
180
+ return null;
181
+ }
182
+ try {
183
+ const raw = fs.readFileSync(profileJsonPath, 'utf-8');
184
+ const profile = JSON.parse(raw);
185
+ return {
186
+ name: profileName,
187
+ baseUrl: profile.baseUrl,
188
+ accountId: profile.id,
189
+ username: profile.user,
190
+ retailers: listProfileRetailers(profileDirPath),
191
+ };
192
+ }
193
+ catch {
194
+ return null;
195
+ }
196
+ }
197
+ function listProfileRetailers(profileDirPath) {
198
+ try {
199
+ const entries = fs.readdirSync(profileDirPath);
200
+ return entries
201
+ .filter(name => name.startsWith('retailer.') && name.endsWith('.json'))
202
+ .map(name => name.slice('retailer.'.length, -'.json'.length))
203
+ .sort();
204
+ }
205
+ catch {
206
+ return [];
207
+ }
208
+ }
@@ -1,6 +1,7 @@
1
1
  import { FluentClient } from './clients/fluent-client';
2
2
  import { FluentVersoriClient } from './versori/fluent-versori-client';
3
3
  import { FluentClientConfig, Logger, ExecutionContext } from './types';
4
+ import { type FluentProfileOptions } from './auth/profile-loader';
4
5
  export type ClientContext = {
5
6
  fetch: typeof fetch;
6
7
  log?: Logger;
@@ -24,3 +25,6 @@ export declare const isDirectContext: (context: ClientContext) => context is {
24
25
  config: FluentClientConfig;
25
26
  logger?: Logger;
26
27
  };
28
+ export declare function createClientFromProfile(profileName: string, options?: FluentProfileOptions & CreateClientOptions & {
29
+ logger?: Logger;
30
+ }): Promise<FluentClient>;
@@ -2,8 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isDirectContext = exports.isHttpContext = void 0;
4
4
  exports.createClient = createClient;
5
+ exports.createClientFromProfile = createClientFromProfile;
5
6
  const fluent_client_1 = require("./clients/fluent-client");
6
7
  const fluent_versori_client_1 = require("./versori/fluent-versori-client");
8
+ const profile_loader_1 = require("./auth/profile-loader");
7
9
  async function createClient(context, options) {
8
10
  const logger = 'log' in context ? context.log : 'logger' in context ? context.logger : undefined;
9
11
  const validateConnection = options?.validateConnection ?? false;
@@ -107,3 +109,11 @@ const isDirectContext = (context) => {
107
109
  return 'config' in context;
108
110
  };
109
111
  exports.isDirectContext = isDirectContext;
112
+ async function createClientFromProfile(profileName, options) {
113
+ const config = (0, profile_loader_1.loadFluentProfile)(profileName, options);
114
+ const client = new fluent_client_1.FluentClient(config, options?.logger);
115
+ if (options?.validateConnection) {
116
+ await client.graphql({ query: 'query { me { ref } }' });
117
+ }
118
+ return client;
119
+ }
@@ -1,4 +1,4 @@
1
- export { createClient, isHttpContext, isDirectContext } from './client-factory';
1
+ export { createClient, createClientFromProfile, isHttpContext, isDirectContext, } from './client-factory';
2
2
  export type { ClientContext, CreateClientOptions } from './client-factory';
3
3
  export { FluentClient } from './clients/fluent-client';
4
4
  export { FluentVersoriClient } from './versori/fluent-versori-client';
@@ -14,6 +14,8 @@ export { DataSourceType } from './types';
14
14
  export { parseAndValidateCsv, detectFileType, parseWebhookRequest, validateFluentEvent, validateGraphQLPayload, generateFileName, generateTimestampedFileName, generateFilePath, generateDateSubdirectories, extractFileName, extractFileExtension, replaceFileExtension, type FileNamingOptions, } from './utils';
15
15
  export { detectPaginationVariables, extractConnection, extractCursor, hasMorePages, buildPaginationVariables, type PaginationVariables, } from './utils/pagination-helpers';
16
16
  export { OAuth2AuthProvider, BearerTokenAuthProvider, AuthManager, VersoriConnectionAdapter, type VersoriConnection, } from './auth/auth-provider';
17
+ export { loadFluentProfile, listFluentProfiles, getFluentProfileInfo, resolveProfileBaseDir, } from './auth/profile-loader';
18
+ export type { FluentProfileOptions, FluentProfileInfo } from './auth/profile-loader';
17
19
  export { type FileDiscoveryStrategy, type FileDiscoveryContext, type FileParserStrategy, type TransformationStrategy, type TransformationContext, type IngestionValidationResult, type BatchSubmissionStrategy, type BatchSubmissionContext, type BatchSubmissionResult, type JobManagementStrategy, type JobContext, type JobStatus, type IngestionStrategy, type StrategyFactory, } from './services/orchestration/strategies/ingestion-strategies';
18
20
  export * from './errors/index';
19
21
  export { ConfigurationError, AggregateIngestionError, IngestionErrorFactory, } from './errors/ingestion-errors';
package/dist/cjs/index.js CHANGED
@@ -14,10 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.XMLBuilder = exports.detectParserFileType = exports.isParserSupported = exports.getSupportedParserTypes = exports.createParser = exports.JSONParserService = exports.XMLParserService = exports.ParquetParserService = exports.CSVParserService = exports.classifyErrors = exports.classifyError = exports.IngestionErrorFactory = exports.AggregateIngestionError = exports.ConfigurationError = exports.VersoriConnectionAdapter = exports.AuthManager = exports.BearerTokenAuthProvider = exports.OAuth2AuthProvider = exports.buildPaginationVariables = exports.hasMorePages = exports.extractCursor = exports.extractConnection = exports.detectPaginationVariables = exports.replaceFileExtension = exports.extractFileExtension = exports.extractFileName = exports.generateDateSubdirectories = exports.generateFilePath = exports.generateTimestampedFileName = exports.generateFileName = exports.validateGraphQLPayload = exports.validateFluentEvent = exports.parseWebhookRequest = exports.detectFileType = exports.parseAndValidateCsv = exports.DataSourceType = exports.Intermediate = exports.SchemaValidationService = exports.SignatureAlgorithm = exports.WebhookValidationError = exports.WebhookValidationFactory = exports.WebhookValidationService = exports.VersoriIndexedFileTracker = exports.VersoriKVAdapter = exports.VersoriFileTracker = exports.FluentVersoriClient = exports.FluentClient = exports.isDirectContext = exports.isHttpContext = exports.createClient = void 0;
18
- exports.VERSION = exports.S3ServiceError = exports.S3Service = exports.FluentConnectionTester = exports.S3ComparisonTester = exports.S3PresignedTester = exports.S3SDKTester = exports.ProcessingStatus = exports.ValidationMode = exports.AwsRegion = exports.EntityType = exports.BatchAction = exports.FileType = exports.JobStrategy = exports.FileEncoding = exports.CsvDelimiter = exports.ResolverError = exports.MappingError = exports.GraphQLTemplateGenerator = exports.GraphQLIntrospectionService = exports.formatErrorSummary = exports.getSuccessfulMutations = exports.getFailedMutations = exports.getErrorMessage = exports.parseAliasedMutationResponse = exports.inferInputTypeName = exports.buildAliasedMutationQuery = exports.buildMutationQuery = exports.GraphQLMutationMapper = exports.createResolverBuilder = exports.ResolverBuilder = exports.getSdkResolverNames = exports.isSdkResolver = exports.getSdkResolver = exports.sdkResolvers = exports.UniversalMapper = exports.UniversalPathResolver = exports.JSONBuilder = exports.CSVBuilder = void 0;
17
+ exports.JSONParserService = exports.XMLParserService = exports.ParquetParserService = exports.CSVParserService = exports.classifyErrors = exports.classifyError = exports.IngestionErrorFactory = exports.AggregateIngestionError = exports.ConfigurationError = exports.resolveProfileBaseDir = exports.getFluentProfileInfo = exports.listFluentProfiles = exports.loadFluentProfile = exports.VersoriConnectionAdapter = exports.AuthManager = exports.BearerTokenAuthProvider = exports.OAuth2AuthProvider = exports.buildPaginationVariables = exports.hasMorePages = exports.extractCursor = exports.extractConnection = exports.detectPaginationVariables = exports.replaceFileExtension = exports.extractFileExtension = exports.extractFileName = exports.generateDateSubdirectories = exports.generateFilePath = exports.generateTimestampedFileName = exports.generateFileName = exports.validateGraphQLPayload = exports.validateFluentEvent = exports.parseWebhookRequest = exports.detectFileType = exports.parseAndValidateCsv = exports.DataSourceType = exports.Intermediate = exports.SchemaValidationService = exports.SignatureAlgorithm = exports.WebhookValidationError = exports.WebhookValidationFactory = exports.WebhookValidationService = exports.VersoriIndexedFileTracker = exports.VersoriKVAdapter = exports.VersoriFileTracker = exports.FluentVersoriClient = exports.FluentClient = exports.isDirectContext = exports.isHttpContext = exports.createClientFromProfile = exports.createClient = void 0;
18
+ exports.VERSION = exports.S3ServiceError = exports.S3Service = exports.FluentConnectionTester = exports.S3ComparisonTester = exports.S3PresignedTester = exports.S3SDKTester = exports.ProcessingStatus = exports.ValidationMode = exports.AwsRegion = exports.EntityType = exports.BatchAction = exports.FileType = exports.JobStrategy = exports.FileEncoding = exports.CsvDelimiter = exports.ResolverError = exports.MappingError = exports.GraphQLTemplateGenerator = exports.GraphQLIntrospectionService = exports.formatErrorSummary = exports.getSuccessfulMutations = exports.getFailedMutations = exports.getErrorMessage = exports.parseAliasedMutationResponse = exports.inferInputTypeName = exports.buildAliasedMutationQuery = exports.buildMutationQuery = exports.GraphQLMutationMapper = exports.createResolverBuilder = exports.ResolverBuilder = exports.getSdkResolverNames = exports.isSdkResolver = exports.getSdkResolver = exports.sdkResolvers = exports.UniversalMapper = exports.UniversalPathResolver = exports.JSONBuilder = exports.CSVBuilder = exports.XMLBuilder = exports.detectParserFileType = exports.isParserSupported = exports.getSupportedParserTypes = exports.createParser = void 0;
19
19
  var client_factory_1 = require("./client-factory");
20
20
  Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return client_factory_1.createClient; } });
21
+ Object.defineProperty(exports, "createClientFromProfile", { enumerable: true, get: function () { return client_factory_1.createClientFromProfile; } });
21
22
  Object.defineProperty(exports, "isHttpContext", { enumerable: true, get: function () { return client_factory_1.isHttpContext; } });
22
23
  Object.defineProperty(exports, "isDirectContext", { enumerable: true, get: function () { return client_factory_1.isDirectContext; } });
23
24
  var fluent_client_1 = require("./clients/fluent-client");
@@ -66,6 +67,11 @@ Object.defineProperty(exports, "OAuth2AuthProvider", { enumerable: true, get: fu
66
67
  Object.defineProperty(exports, "BearerTokenAuthProvider", { enumerable: true, get: function () { return auth_provider_1.BearerTokenAuthProvider; } });
67
68
  Object.defineProperty(exports, "AuthManager", { enumerable: true, get: function () { return auth_provider_1.AuthManager; } });
68
69
  Object.defineProperty(exports, "VersoriConnectionAdapter", { enumerable: true, get: function () { return auth_provider_1.VersoriConnectionAdapter; } });
70
+ var profile_loader_1 = require("./auth/profile-loader");
71
+ Object.defineProperty(exports, "loadFluentProfile", { enumerable: true, get: function () { return profile_loader_1.loadFluentProfile; } });
72
+ Object.defineProperty(exports, "listFluentProfiles", { enumerable: true, get: function () { return profile_loader_1.listFluentProfiles; } });
73
+ Object.defineProperty(exports, "getFluentProfileInfo", { enumerable: true, get: function () { return profile_loader_1.getFluentProfileInfo; } });
74
+ Object.defineProperty(exports, "resolveProfileBaseDir", { enumerable: true, get: function () { return profile_loader_1.resolveProfileBaseDir; } });
69
75
  __exportStar(require("./errors/index"), exports);
70
76
  var ingestion_errors_1 = require("./errors/ingestion-errors");
71
77
  Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return ingestion_errors_1.ConfigurationError; } });
@@ -133,7 +133,7 @@ export interface FluentEventLogContext {
133
133
  }
134
134
  export interface FluentEventLogAttribute {
135
135
  name: string;
136
- value: string;
136
+ value: unknown;
137
137
  type?: string;
138
138
  [key: string]: unknown;
139
139
  }
@@ -0,0 +1,3 @@
1
+ export { loadFluentProfile, listFluentProfiles, getFluentProfileInfo, resolveProfileBaseDir, } from './profile-loader.js';
2
+ export type { FluentProfileOptions, FluentProfileInfo } from './profile-loader.js';
3
+ export { OAuth2AuthProvider, BearerTokenAuthProvider, AuthManager, VersoriConnectionAdapter, type VersoriConnection, } from './auth-provider.js';
@@ -0,0 +1,2 @@
1
+ export { loadFluentProfile, listFluentProfiles, getFluentProfileInfo, resolveProfileBaseDir, } from './profile-loader.js';
2
+ export { OAuth2AuthProvider, BearerTokenAuthProvider, AuthManager, VersoriConnectionAdapter, } from './auth-provider.js';
@@ -0,0 +1,18 @@
1
+ import { FluentClientConfig } from '../types/index.js';
2
+ export interface FluentProfileOptions {
3
+ profileDir?: string;
4
+ retailer?: string;
5
+ timeout?: number;
6
+ retryConfig?: FluentClientConfig['retryConfig'];
7
+ }
8
+ export interface FluentProfileInfo {
9
+ name: string;
10
+ baseUrl: string;
11
+ accountId: string;
12
+ username: string;
13
+ retailers: string[];
14
+ }
15
+ export declare function resolveProfileBaseDir(options?: FluentProfileOptions): string;
16
+ export declare function loadFluentProfile(profileName: string, options?: FluentProfileOptions): FluentClientConfig;
17
+ export declare function listFluentProfiles(options?: FluentProfileOptions): string[];
18
+ export declare function getFluentProfileInfo(profileName: string, options?: FluentProfileOptions): FluentProfileInfo | null;
@@ -0,0 +1,169 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ export function resolveProfileBaseDir(options) {
5
+ if (options?.profileDir) {
6
+ return options.profileDir;
7
+ }
8
+ const envDir = process.env.FLUENT_PROFILE_DIR;
9
+ if (envDir) {
10
+ return envDir;
11
+ }
12
+ return path.join(os.homedir(), '.fluentcommerce');
13
+ }
14
+ export function loadFluentProfile(profileName, options) {
15
+ if (!profileName || typeof profileName !== 'string') {
16
+ throw new Error('Profile name is required');
17
+ }
18
+ const baseDir = resolveProfileBaseDir(options);
19
+ const profileDir = path.join(baseDir, profileName);
20
+ if (!fs.existsSync(profileDir)) {
21
+ const available = listFluentProfiles(options);
22
+ throw new Error(`Fluent CLI profile '${profileName}' not found at ${profileDir}. ` +
23
+ (available.length > 0
24
+ ? `Available profiles: ${available.join(', ')}`
25
+ : `No profiles found in ${baseDir}. Create one with: fluent profile create ${profileName}`));
26
+ }
27
+ const profileJsonPath = path.join(profileDir, 'profile.json');
28
+ if (!fs.existsSync(profileJsonPath)) {
29
+ throw new Error(`profile.json not found in ${profileDir}. ` +
30
+ `This directory may not be a valid Fluent CLI profile.`);
31
+ }
32
+ let profile;
33
+ try {
34
+ const raw = fs.readFileSync(profileJsonPath, 'utf-8');
35
+ profile = JSON.parse(raw);
36
+ }
37
+ catch (err) {
38
+ throw new Error(`Failed to read ${profileJsonPath}: ${err instanceof Error ? err.message : String(err)}`);
39
+ }
40
+ if (!profile.baseUrl) {
41
+ throw new Error(`Profile '${profileName}' is missing required field 'baseUrl' in profile.json`);
42
+ }
43
+ if (!profile.id) {
44
+ throw new Error(`Profile '${profileName}' is missing required field 'id' in profile.json`);
45
+ }
46
+ if (!profile.clientSecret) {
47
+ throw new Error(`Profile '${profileName}' is missing required field 'clientSecret' in profile.json`);
48
+ }
49
+ if (!profile.user) {
50
+ throw new Error(`Profile '${profileName}' is missing required field 'user' in profile.json`);
51
+ }
52
+ let retailerId;
53
+ let resolvedUser = profile.user;
54
+ if (options?.retailer) {
55
+ const retailerJsonPath = path.join(profileDir, `retailer.${options.retailer}.json`);
56
+ if (!fs.existsSync(retailerJsonPath)) {
57
+ const availableRetailers = listProfileRetailers(profileDir);
58
+ throw new Error(`Retailer '${options.retailer}' not found for profile '${profileName}' at ${retailerJsonPath}. ` +
59
+ (availableRetailers.length > 0
60
+ ? `Available retailers: ${availableRetailers.join(', ')}`
61
+ : `No retailer files found in ${profileDir}.`));
62
+ }
63
+ let retailer;
64
+ try {
65
+ const raw = fs.readFileSync(retailerJsonPath, 'utf-8');
66
+ retailer = JSON.parse(raw);
67
+ }
68
+ catch (err) {
69
+ throw new Error(`Failed to read ${retailerJsonPath}: ${err instanceof Error ? err.message : String(err)}`);
70
+ }
71
+ retailerId = retailer.id;
72
+ if (retailer.user) {
73
+ resolvedUser = retailer.user;
74
+ }
75
+ }
76
+ const userJsonPath = path.join(profileDir, `user.${resolvedUser}.json`);
77
+ if (!fs.existsSync(userJsonPath)) {
78
+ throw new Error(`User credentials file not found: ${userJsonPath}. ` +
79
+ `Expected file for user '${resolvedUser}'` +
80
+ (options?.retailer
81
+ ? ` (set by retailer '${options.retailer}').`
82
+ : ` (set in profile.json).`));
83
+ }
84
+ let userCreds;
85
+ try {
86
+ const raw = fs.readFileSync(userJsonPath, 'utf-8');
87
+ userCreds = JSON.parse(raw);
88
+ }
89
+ catch (err) {
90
+ throw new Error(`Failed to read ${userJsonPath}: ${err instanceof Error ? err.message : String(err)}`);
91
+ }
92
+ if (!userCreds.username || !userCreds.password) {
93
+ throw new Error(`User credentials file ${userJsonPath} is missing 'username' or 'password' field`);
94
+ }
95
+ const config = {
96
+ baseUrl: profile.baseUrl,
97
+ clientId: profile.id,
98
+ clientSecret: profile.clientSecret,
99
+ username: userCreds.username,
100
+ password: userCreds.password,
101
+ };
102
+ if (retailerId) {
103
+ config.retailerId = retailerId;
104
+ }
105
+ if (options?.timeout) {
106
+ config.timeout = options.timeout;
107
+ }
108
+ if (options?.retryConfig) {
109
+ config.retryConfig = options.retryConfig;
110
+ }
111
+ return config;
112
+ }
113
+ export function listFluentProfiles(options) {
114
+ const baseDir = resolveProfileBaseDir(options);
115
+ if (!fs.existsSync(baseDir)) {
116
+ return [];
117
+ }
118
+ try {
119
+ const entries = fs.readdirSync(baseDir, { withFileTypes: true });
120
+ return entries
121
+ .filter(entry => {
122
+ if (!entry.isDirectory())
123
+ return false;
124
+ if (entry.name.startsWith('.'))
125
+ return false;
126
+ const profileJson = path.join(baseDir, entry.name, 'profile.json');
127
+ return fs.existsSync(profileJson);
128
+ })
129
+ .map(entry => entry.name)
130
+ .sort();
131
+ }
132
+ catch {
133
+ return [];
134
+ }
135
+ }
136
+ export function getFluentProfileInfo(profileName, options) {
137
+ const baseDir = resolveProfileBaseDir(options);
138
+ const profileDirPath = path.join(baseDir, profileName);
139
+ const profileJsonPath = path.join(profileDirPath, 'profile.json');
140
+ if (!fs.existsSync(profileJsonPath)) {
141
+ return null;
142
+ }
143
+ try {
144
+ const raw = fs.readFileSync(profileJsonPath, 'utf-8');
145
+ const profile = JSON.parse(raw);
146
+ return {
147
+ name: profileName,
148
+ baseUrl: profile.baseUrl,
149
+ accountId: profile.id,
150
+ username: profile.user,
151
+ retailers: listProfileRetailers(profileDirPath),
152
+ };
153
+ }
154
+ catch {
155
+ return null;
156
+ }
157
+ }
158
+ function listProfileRetailers(profileDirPath) {
159
+ try {
160
+ const entries = fs.readdirSync(profileDirPath);
161
+ return entries
162
+ .filter(name => name.startsWith('retailer.') && name.endsWith('.json'))
163
+ .map(name => name.slice('retailer.'.length, -'.json'.length))
164
+ .sort();
165
+ }
166
+ catch {
167
+ return [];
168
+ }
169
+ }
@@ -1,6 +1,7 @@
1
1
  import { FluentClient } from './clients/fluent-client.js';
2
2
  import { FluentVersoriClient } from './versori/fluent-versori-client.js';
3
3
  import { FluentClientConfig, Logger, ExecutionContext } from './types/index.js';
4
+ import { type FluentProfileOptions } from './auth/profile-loader.js';
4
5
  export type ClientContext = {
5
6
  fetch: typeof fetch;
6
7
  log?: Logger;
@@ -24,3 +25,6 @@ export declare const isDirectContext: (context: ClientContext) => context is {
24
25
  config: FluentClientConfig;
25
26
  logger?: Logger;
26
27
  };
28
+ export declare function createClientFromProfile(profileName: string, options?: FluentProfileOptions & CreateClientOptions & {
29
+ logger?: Logger;
30
+ }): Promise<FluentClient>;