@thorprovider/adapters 4.2.1 → 5.0.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.
Files changed (60) hide show
  1. package/PROVIDER_IMPLEMENTATION_ANALYSIS.md +659 -0
  2. package/dist/index.d.mts +225 -387
  3. package/dist/index.d.ts +225 -387
  4. package/dist/index.js +613 -3062
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +609 -3052
  7. package/dist/index.mjs.map +1 -1
  8. package/docs/RETRY_SOLUTION_SUMMARY.md +26 -11
  9. package/docs/authentication.md +115 -62
  10. package/docs/capabilities-system.md +4 -2
  11. package/docs/category-customization.md +37 -29
  12. package/docs/fetch-reliability-solution.md +127 -278
  13. package/docs/getting-started.md +43 -29
  14. package/docs/profile-management.md +68 -38
  15. package/docs/services-overview.md +4 -2
  16. package/package.json +1 -1
  17. package/quick-reference.md +33 -0
  18. package/src/backend/factory.ts +1 -11
  19. package/src/factory/index.ts +74 -52
  20. package/src/index.ts +5 -15
  21. package/src/providers/index.ts +1 -16
  22. package/src/providers/medusa/admin.test.ts +404 -0
  23. package/src/providers/medusa/admin.ts +442 -0
  24. package/src/providers/medusa/auth.test.ts +137 -0
  25. package/src/providers/medusa/auth.ts +96 -1
  26. package/src/providers/medusa/index.ts +21 -1
  27. package/src/resilience/README.md +70 -48
  28. package/src/types/capabilities.types.ts +0 -250
  29. package/src/core/errors.js +0 -161
  30. package/src/core/fetch-utils.js +0 -240
  31. package/src/core/logger.js +0 -63
  32. package/src/metadata/channel-scope-validation.js +0 -136
  33. package/src/metadata/channel-scope.js +0 -174
  34. package/src/metadata/index.js +0 -31
  35. package/src/providers/bigcommerce/bigcommerce.ts +0 -6
  36. package/src/providers/bigcommerce/index.ts +0 -315
  37. package/src/providers/bigcommerce/transforms.ts +0 -227
  38. package/src/providers/magento/index.ts +0 -387
  39. package/src/providers/magento/magento.ts +0 -5
  40. package/src/providers/magento/queries.ts +0 -267
  41. package/src/providers/magento/transforms.ts +0 -249
  42. package/src/providers/medusa/auth.js +0 -601
  43. package/src/providers/medusa/index.js +0 -2485
  44. package/src/providers/medusa/payment.js +0 -265
  45. package/src/providers/medusa/transforms.js +0 -505
  46. package/src/providers/shopify/auth.ts +0 -402
  47. package/src/providers/shopify/index.ts +0 -444
  48. package/src/providers/shopify/mutations.ts +0 -247
  49. package/src/providers/shopify/queries.ts +0 -324
  50. package/src/providers/shopify/shopify.ts +0 -7
  51. package/src/providers/shopify/transforms.ts +0 -275
  52. package/src/providers/spree/index.ts +0 -344
  53. package/src/providers/spree/spree.ts +0 -5
  54. package/src/providers/spree/transforms.ts +0 -254
  55. package/src/providers/woocommerce/index.ts +0 -310
  56. package/src/providers/woocommerce/transforms.ts +0 -254
  57. package/src/providers/woocommerce/woocommerce.ts +0 -5
  58. package/src/types/capabilities.types.js +0 -141
  59. package/src/types/provider.js +0 -9
  60. package/src/utils/payment-utils.js +0 -152
@@ -1,161 +0,0 @@
1
- "use strict";
2
- /**
3
- * @thorprovider/adapters v2.0
4
- * Core errors
5
- */
6
- var __extends = (this && this.__extends) || (function () {
7
- var extendStatics = function (d, b) {
8
- extendStatics = Object.setPrototypeOf ||
9
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
10
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
11
- return extendStatics(d, b);
12
- };
13
- return function (d, b) {
14
- if (typeof b !== "function" && b !== null)
15
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
16
- extendStatics(d, b);
17
- function __() { this.constructor = d; }
18
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
19
- };
20
- })();
21
- Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.ProviderAPIError = exports.ResourceNotFoundError = exports.ConfigurationError = exports.ProviderNotFoundError = exports.CommerceError = void 0;
23
- exports.extractErrorMessage = extractErrorMessage;
24
- /**
25
- * Base error class for commerce providers
26
- */
27
- var CommerceError = /** @class */ (function (_super) {
28
- __extends(CommerceError, _super);
29
- function CommerceError(message, code, status) {
30
- var _this = _super.call(this, message) || this;
31
- _this.code = code;
32
- _this.status = status;
33
- _this.name = 'CommerceError';
34
- return _this;
35
- }
36
- return CommerceError;
37
- }(Error));
38
- exports.CommerceError = CommerceError;
39
- /**
40
- * Provider not found error
41
- */
42
- var ProviderNotFoundError = /** @class */ (function (_super) {
43
- __extends(ProviderNotFoundError, _super);
44
- function ProviderNotFoundError(providerName) {
45
- var _this = _super.call(this, "Provider not found: ".concat(providerName), 'PROVIDER_NOT_FOUND') || this;
46
- _this.name = 'ProviderNotFoundError';
47
- return _this;
48
- }
49
- return ProviderNotFoundError;
50
- }(CommerceError));
51
- exports.ProviderNotFoundError = ProviderNotFoundError;
52
- /**
53
- * Configuration error
54
- */
55
- var ConfigurationError = /** @class */ (function (_super) {
56
- __extends(ConfigurationError, _super);
57
- function ConfigurationError(message) {
58
- var _this = _super.call(this, message, 'CONFIGURATION_ERROR') || this;
59
- _this.name = 'ConfigurationError';
60
- return _this;
61
- }
62
- return ConfigurationError;
63
- }(CommerceError));
64
- exports.ConfigurationError = ConfigurationError;
65
- /**
66
- * Resource not found error
67
- */
68
- var ResourceNotFoundError = /** @class */ (function (_super) {
69
- __extends(ResourceNotFoundError, _super);
70
- function ResourceNotFoundError(resource, identifier) {
71
- var _this = _super.call(this, "".concat(resource, " not found: ").concat(identifier), 'RESOURCE_NOT_FOUND', 404) || this;
72
- _this.name = 'ResourceNotFoundError';
73
- return _this;
74
- }
75
- return ResourceNotFoundError;
76
- }(CommerceError));
77
- exports.ResourceNotFoundError = ResourceNotFoundError;
78
- /**
79
- * API error from provider
80
- */
81
- var ProviderAPIError = /** @class */ (function (_super) {
82
- __extends(ProviderAPIError, _super);
83
- function ProviderAPIError(message, provider, status) {
84
- var _this = _super.call(this, message, 'PROVIDER_API_ERROR', status) || this;
85
- _this.provider = provider;
86
- _this.name = 'ProviderAPIError';
87
- return _this;
88
- }
89
- return ProviderAPIError;
90
- }(CommerceError));
91
- exports.ProviderAPIError = ProviderAPIError;
92
- /**
93
- * Extract error message from various error types
94
- * Handles standard Error objects, API responses, and unknown error types
95
- */
96
- function extractErrorMessage(error) {
97
- var _a, _b, _c, _d, _e, _f;
98
- // Standard Error object
99
- if (error instanceof Error && error.message) {
100
- // Don't return generic messages if there's more data
101
- if (error.message !== 'An unknown error occurred.') {
102
- return error.message;
103
- }
104
- }
105
- // Object with message property
106
- if (typeof error === 'object' && error !== null) {
107
- var err = error;
108
- // Check for response body (common in fetch errors)
109
- if (err.body) {
110
- if (typeof err.body === 'string')
111
- return err.body;
112
- if (err.body.message)
113
- return String(err.body.message);
114
- if ((_a = err.body.error) === null || _a === void 0 ? void 0 : _a.message)
115
- return String(err.body.error.message);
116
- }
117
- // Check for response object
118
- if (err.response) {
119
- if ((_b = err.response.data) === null || _b === void 0 ? void 0 : _b.message)
120
- return String(err.response.data.message);
121
- if ((_c = err.response.data) === null || _c === void 0 ? void 0 : _c.error)
122
- return String(err.response.data.error);
123
- if (err.response.statusText)
124
- return "".concat(err.response.status || '', " ").concat(err.response.statusText).trim();
125
- }
126
- // Check for data property
127
- if ((_d = err.data) === null || _d === void 0 ? void 0 : _d.message)
128
- return String(err.data.message);
129
- if ((_e = err.data) === null || _e === void 0 ? void 0 : _e.error)
130
- return String(err.data.error);
131
- // Check for error property
132
- if ((_f = err.error) === null || _f === void 0 ? void 0 : _f.message)
133
- return String(err.error.message);
134
- // Check for statusText with status code
135
- if (err.status && err.statusText) {
136
- return "HTTP ".concat(err.status, ": ").concat(err.statusText);
137
- }
138
- if (err.status && !err.statusText) {
139
- return "HTTP ".concat(err.status, " Error");
140
- }
141
- // Check for message property (not generic)
142
- if (err.message && err.message !== 'An unknown error occurred.') {
143
- return String(err.message);
144
- }
145
- // Try to stringify the error object
146
- try {
147
- var stringified = JSON.stringify(error);
148
- if (stringified !== '{}')
149
- return stringified;
150
- }
151
- catch (_g) {
152
- // Ignore JSON.stringify errors
153
- }
154
- }
155
- // String error
156
- if (typeof error === 'string') {
157
- return error;
158
- }
159
- // Fallback
160
- return 'An unknown error occurred';
161
- }
@@ -1,240 +0,0 @@
1
- "use strict";
2
- /**
3
- * @thorprovider/adapters v2.0
4
- * Fetch Utilities - Retry logic, timeouts, and error handling
5
- *
6
- * This module provides enhanced fetch capabilities with:
7
- * - Automatic retries with exponential backoff
8
- * - Configurable timeouts
9
- * - Better error handling for network failures
10
- */
11
- var __assign = (this && this.__assign) || function () {
12
- __assign = Object.assign || function(t) {
13
- for (var s, i = 1, n = arguments.length; i < n; i++) {
14
- s = arguments[i];
15
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
16
- t[p] = s[p];
17
- }
18
- return t;
19
- };
20
- return __assign.apply(this, arguments);
21
- };
22
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
23
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
24
- return new (P || (P = Promise))(function (resolve, reject) {
25
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
26
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
27
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
28
- step((generator = generator.apply(thisArg, _arguments || [])).next());
29
- });
30
- };
31
- var __generator = (this && this.__generator) || function (thisArg, body) {
32
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
33
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
34
- function verb(n) { return function (v) { return step([n, v]); }; }
35
- function step(op) {
36
- if (f) throw new TypeError("Generator is already executing.");
37
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
38
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
39
- if (y = 0, t) op = [op[0] & 2, t.value];
40
- switch (op[0]) {
41
- case 0: case 1: t = op; break;
42
- case 4: _.label++; return { value: op[1], done: false };
43
- case 5: _.label++; y = op[1]; op = [0]; continue;
44
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
45
- default:
46
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
47
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
48
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
49
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
50
- if (t[2]) _.ops.pop();
51
- _.trys.pop(); continue;
52
- }
53
- op = body.call(thisArg, _);
54
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
55
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
56
- }
57
- };
58
- Object.defineProperty(exports, "__esModule", { value: true });
59
- exports.fetchWithRetry = fetchWithRetry;
60
- exports.createFetchWithRetry = createFetchWithRetry;
61
- var logger_1 = require("./logger");
62
- var logger = (0, logger_1.createLogger)('FetchUtils');
63
- var DEFAULT_RETRY_CONFIG = {
64
- maxRetries: 3,
65
- initialDelayMs: 300,
66
- backoffMultiplier: 2,
67
- maxDelayMs: 5000,
68
- timeoutMs: 10000, // 10 seconds
69
- retryableStatusCodes: [408, 429, 500, 502, 503, 504],
70
- };
71
- /**
72
- * Sleep utility for delays between retries
73
- */
74
- function sleep(ms) {
75
- return new Promise(function (resolve) { return setTimeout(resolve, ms); });
76
- }
77
- /**
78
- * Calculate delay for exponential backoff
79
- */
80
- function calculateDelay(attempt, config) {
81
- var delay = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt);
82
- return Math.min(delay, config.maxDelayMs);
83
- }
84
- /**
85
- * Check if error is retryable
86
- */
87
- function isRetryableError(error, config) {
88
- var _a, _b;
89
- // Network errors (fetch failed, connection refused, etc.)
90
- if (error.name === 'TypeError' || ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('fetch failed'))) {
91
- return true;
92
- }
93
- // Timeout errors
94
- if (error.name === 'AbortError' || ((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('timeout'))) {
95
- return true;
96
- }
97
- // HTTP status codes
98
- if (error.status && config.retryableStatusCodes.includes(error.status)) {
99
- return true;
100
- }
101
- return false;
102
- }
103
- /**
104
- * Enhanced fetch with retry logic and timeout
105
- *
106
- * @example
107
- * ```typescript
108
- * import { fetchWithRetry } from '@thorprovider/adapters/core/fetch-utils';
109
- *
110
- * // Basic usage with defaults (3 retries, 10s timeout)
111
- * const response = await fetchWithRetry('https://api.example.com/products');
112
- *
113
- * // Custom configuration
114
- * const response = await fetchWithRetry(
115
- * 'https://api.example.com/products',
116
- * {
117
- * method: 'POST',
118
- * body: JSON.stringify({ name: 'Product' }),
119
- * },
120
- * {
121
- * maxRetries: 5,
122
- * timeoutMs: 15000, // 15 seconds
123
- * initialDelayMs: 500,
124
- * }
125
- * );
126
- * ```
127
- */
128
- function fetchWithRetry(url, init, retryConfig) {
129
- return __awaiter(this, void 0, void 0, function () {
130
- var config, lastError, _loop_1, attempt, state_1;
131
- return __generator(this, function (_a) {
132
- switch (_a.label) {
133
- case 0:
134
- config = __assign(__assign({}, DEFAULT_RETRY_CONFIG), retryConfig);
135
- _loop_1 = function (attempt) {
136
- var controller_1, timeoutId, fetchOptions, response, error, error_1, shouldRetry, delay;
137
- return __generator(this, function (_b) {
138
- switch (_b.label) {
139
- case 0:
140
- _b.trys.push([0, 2, , 6]);
141
- controller_1 = new AbortController();
142
- timeoutId = setTimeout(function () { return controller_1.abort(); }, config.timeoutMs);
143
- fetchOptions = __assign(__assign({}, init), { signal: controller_1.signal });
144
- logger.debug('Fetch attempt', {
145
- url: url,
146
- attempt: attempt + 1,
147
- maxAttempts: config.maxRetries + 1,
148
- timeout: config.timeoutMs,
149
- });
150
- return [4 /*yield*/, fetch(url, fetchOptions)];
151
- case 1:
152
- response = _b.sent();
153
- // Clear timeout
154
- clearTimeout(timeoutId);
155
- // Check if status code is retryable
156
- if (!response.ok && config.retryableStatusCodes.includes(response.status)) {
157
- error = new Error("HTTP ".concat(response.status, ": ").concat(response.statusText));
158
- error.status = response.status;
159
- throw error;
160
- }
161
- // Success!
162
- logger.debug('Fetch successful', { url: url, attempt: attempt + 1 });
163
- return [2 /*return*/, { value: response }];
164
- case 2:
165
- error_1 = _b.sent();
166
- lastError = error_1;
167
- // Clear timeout if it exists
168
- if (error_1.name === 'AbortError') {
169
- lastError = new Error("Request timeout after ".concat(config.timeoutMs, "ms"));
170
- lastError.name = 'TimeoutError';
171
- }
172
- shouldRetry = attempt < config.maxRetries && isRetryableError(error_1, config);
173
- if (!shouldRetry) return [3 /*break*/, 4];
174
- delay = calculateDelay(attempt, config);
175
- logger.warn('Fetch failed, retrying...', {
176
- url: url,
177
- attempt: attempt + 1,
178
- maxAttempts: config.maxRetries + 1,
179
- nextRetryIn: "".concat(delay, "ms"),
180
- error: error_1.message,
181
- });
182
- return [4 /*yield*/, sleep(delay)];
183
- case 3:
184
- _b.sent();
185
- return [3 /*break*/, 5];
186
- case 4:
187
- logger.error('Fetch failed after all retries', {
188
- url: url,
189
- totalAttempts: attempt + 1,
190
- error: error_1.message,
191
- });
192
- return [2 /*return*/, "break"];
193
- case 5: return [3 /*break*/, 6];
194
- case 6: return [2 /*return*/];
195
- }
196
- });
197
- };
198
- attempt = 0;
199
- _a.label = 1;
200
- case 1:
201
- if (!(attempt <= config.maxRetries)) return [3 /*break*/, 4];
202
- return [5 /*yield**/, _loop_1(attempt)];
203
- case 2:
204
- state_1 = _a.sent();
205
- if (typeof state_1 === "object")
206
- return [2 /*return*/, state_1.value];
207
- if (state_1 === "break")
208
- return [3 /*break*/, 4];
209
- _a.label = 3;
210
- case 3:
211
- attempt++;
212
- return [3 /*break*/, 1];
213
- case 4:
214
- // All retries exhausted, throw the last error
215
- throw lastError;
216
- }
217
- });
218
- });
219
- }
220
- /**
221
- * Create a fetch function with custom retry configuration
222
- * Useful for creating a configured fetch instance
223
- *
224
- * @example
225
- * ```typescript
226
- * import { createFetchWithRetry } from '@thorprovider/adapters/core/fetch-utils';
227
- *
228
- * // Create configured fetch
229
- * const customFetch = createFetchWithRetry({
230
- * maxRetries: 5,
231
- * timeoutMs: 15000,
232
- * });
233
- *
234
- * // Use it like regular fetch
235
- * const response = await customFetch('https://api.example.com/products');
236
- * ```
237
- */
238
- function createFetchWithRetry(retryConfig) {
239
- return function (url, init) { return fetchWithRetry(url, init, retryConfig); };
240
- }
@@ -1,63 +0,0 @@
1
- "use strict";
2
- /**
3
- * @thorprovider/adapters v2.0
4
- * Core logger utility
5
- */
6
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
7
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
8
- if (ar || !(i in from)) {
9
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
10
- ar[i] = from[i];
11
- }
12
- }
13
- return to.concat(ar || Array.prototype.slice.call(from));
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.createLogger = createLogger;
17
- /**
18
- * Create a logger instance
19
- */
20
- function createLogger(namespace, enabled) {
21
- if (enabled === void 0) { enabled = false; }
22
- var log = function (level, message) {
23
- var args = [];
24
- for (var _i = 2; _i < arguments.length; _i++) {
25
- args[_i - 2] = arguments[_i];
26
- }
27
- if (!enabled && level !== 'error')
28
- return;
29
- var timestamp = new Date().toISOString();
30
- var prefix = "[".concat(timestamp, "] [@thorprovider/adapters:").concat(namespace, "] ").concat(level.toUpperCase(), ":");
31
- console[level].apply(console, __spreadArray([prefix, message], args, false));
32
- };
33
- return {
34
- debug: function (message) {
35
- var args = [];
36
- for (var _i = 1; _i < arguments.length; _i++) {
37
- args[_i - 1] = arguments[_i];
38
- }
39
- return log.apply(void 0, __spreadArray(['debug', message], args, false));
40
- },
41
- info: function (message) {
42
- var args = [];
43
- for (var _i = 1; _i < arguments.length; _i++) {
44
- args[_i - 1] = arguments[_i];
45
- }
46
- return log.apply(void 0, __spreadArray(['info', message], args, false));
47
- },
48
- warn: function (message) {
49
- var args = [];
50
- for (var _i = 1; _i < arguments.length; _i++) {
51
- args[_i - 1] = arguments[_i];
52
- }
53
- return log.apply(void 0, __spreadArray(['warn', message], args, false));
54
- },
55
- error: function (message) {
56
- var args = [];
57
- for (var _i = 1; _i < arguments.length; _i++) {
58
- args[_i - 1] = arguments[_i];
59
- }
60
- return log.apply(void 0, __spreadArray(['error', message], args, false));
61
- },
62
- };
63
- }
@@ -1,136 +0,0 @@
1
- "use strict";
2
- /**
3
- * Channel Scope Validation — Pure TypeScript (no external deps)
4
- *
5
- * Validates channel-scope metadata before it is written to the backend.
6
- * Keeps all validation logic in L2 so that L5 starters don't duplicate checks.
7
- *
8
- * @module metadata/channel-scope-validation
9
- */
10
- var __extends = (this && this.__extends) || (function () {
11
- var extendStatics = function (d, b) {
12
- extendStatics = Object.setPrototypeOf ||
13
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
14
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
15
- return extendStatics(d, b);
16
- };
17
- return function (d, b) {
18
- if (typeof b !== "function" && b !== null)
19
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
20
- extendStatics(d, b);
21
- function __() { this.constructor = d; }
22
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
23
- };
24
- })();
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.ChannelScopeValidationError = void 0;
27
- exports.validateChannelId = validateChannelId;
28
- exports.validateEntityId = validateEntityId;
29
- exports.validateAssignedBy = validateAssignedBy;
30
- exports.validateChannelScopeAssignment = validateChannelScopeAssignment;
31
- exports.assertEntityBelongsToChannel = assertEntityBelongsToChannel;
32
- var channel_scope_1 = require("./channel-scope");
33
- // ────────────────────────────────────────────────────────────────────
34
- // Errors
35
- // ────────────────────────────────────────────────────────────────────
36
- /**
37
- * Thrown when channel-scope validation fails.
38
- *
39
- * Consumers can catch this specific type to provide user-friendly feedback
40
- * without leaking internal details.
41
- */
42
- var ChannelScopeValidationError = /** @class */ (function (_super) {
43
- __extends(ChannelScopeValidationError, _super);
44
- function ChannelScopeValidationError(message, field, value) {
45
- var _this = _super.call(this, message) || this;
46
- _this.name = 'ChannelScopeValidationError';
47
- _this.field = field;
48
- _this.value = value;
49
- return _this;
50
- }
51
- return ChannelScopeValidationError;
52
- }(Error));
53
- exports.ChannelScopeValidationError = ChannelScopeValidationError;
54
- // ────────────────────────────────────────────────────────────────────
55
- // Validation functions
56
- // ────────────────────────────────────────────────────────────────────
57
- /**
58
- * Validate that a channel ID is a non-empty string.
59
- *
60
- * Does **not** enforce a specific prefix (e.g. `"sc_"`) because different
61
- * backends use different conventions:
62
- * - Medusa: `"sc_01HXXX"`
63
- * - Shopify: `"gid://shopify/Channel/789"`
64
- * - WooCommerce: `"1"` (numeric string)
65
- *
66
- * @throws {ChannelScopeValidationError}
67
- */
68
- function validateChannelId(channelId) {
69
- if (typeof channelId !== 'string' || channelId.trim().length === 0) {
70
- throw new ChannelScopeValidationError('Channel ID must be a non-empty string', 'channelId', channelId);
71
- }
72
- // Sanity upper-bound for ID length (guards against payload abuse)
73
- if (channelId.length > 256) {
74
- throw new ChannelScopeValidationError('Channel ID exceeds maximum length (256)', 'channelId', channelId);
75
- }
76
- }
77
- /**
78
- * Validate that an entity ID is a non-empty string.
79
- *
80
- * @throws {ChannelScopeValidationError}
81
- */
82
- function validateEntityId(entityId, label) {
83
- if (label === void 0) { label = 'entityId'; }
84
- if (typeof entityId !== 'string' || entityId.trim().length === 0) {
85
- throw new ChannelScopeValidationError("".concat(label, " must be a non-empty string"), label, entityId);
86
- }
87
- }
88
- /**
89
- * Validate that the `assignedBy` value (if provided) is a reasonable string.
90
- *
91
- * @throws {ChannelScopeValidationError}
92
- */
93
- function validateAssignedBy(assignedBy) {
94
- if (assignedBy === undefined || assignedBy === null)
95
- return;
96
- if (typeof assignedBy !== 'string') {
97
- throw new ChannelScopeValidationError('assignedBy must be a string', 'assignedBy', assignedBy);
98
- }
99
- if (assignedBy.length > 512) {
100
- throw new ChannelScopeValidationError('assignedBy exceeds maximum length (512)', 'assignedBy', assignedBy);
101
- }
102
- }
103
- /**
104
- * Full validation for a channel-scope assignment operation.
105
- *
106
- * Call before writing channel scope metadata to any entity.
107
- *
108
- * @throws {ChannelScopeValidationError} with descriptive field + value
109
- *
110
- * @example
111
- * ```typescript
112
- * validateChannelScopeAssignment('cus_123', 'sc_01HXXX', 'admin@store.com');
113
- * // If no error, safe to proceed
114
- * ```
115
- */
116
- function validateChannelScopeAssignment(entityId, channelId, assignedBy) {
117
- validateEntityId(entityId, 'entityId');
118
- validateChannelId(channelId);
119
- validateAssignedBy(assignedBy);
120
- }
121
- /**
122
- * Validate that an entity is currently scoped to the expected channel.
123
- *
124
- * Useful as a guard before performing operations that should only
125
- * affect entities within the caller's channel (e.g. a dropshipper editing
126
- * a customer that must belong to their channel).
127
- *
128
- * @throws {ChannelScopeValidationError} if entity does not belong to expectedChannelId
129
- */
130
- function assertEntityBelongsToChannel(entity, expectedChannelId) {
131
- var _a;
132
- var actual = (_a = entity.metadata) === null || _a === void 0 ? void 0 : _a[channel_scope_1.CHANNEL_SCOPE_KEY];
133
- if (actual !== expectedChannelId) {
134
- throw new ChannelScopeValidationError("Entity \"".concat(entity.id, "\" does not belong to channel \"").concat(expectedChannelId, "\" (actual: \"").concat(actual !== null && actual !== void 0 ? actual : 'none', "\")"), channel_scope_1.CHANNEL_SCOPE_KEY, actual);
135
- }
136
- }