@thorprovider/adapters 4.0.1 → 4.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/.turbo/turbo-build.log +18 -18
- package/CHANGELOG.md +22 -0
- package/README.md +17 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.mts +82 -1
- package/dist/index.d.ts +82 -1
- package/dist/index.js +1036 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1034 -0
- package/dist/index.mjs.map +1 -1
- package/docs/getting-started.md +32 -11
- package/docs/providers-overview.md +9 -3
- package/package.json +2 -2
- package/src/factory/index.ts +4 -0
- package/src/index.ts +3 -0
- package/src/lib/auth-storage.ts +101 -0
- package/src/providers/index.ts +3 -0
- package/src/providers/mock/index.ts +427 -0
- package/src/providers/mock/mock.ts +917 -0
- package/src/providers/shopify/auth.ts +402 -0
- package/src/providers/shopify/mutations.ts +145 -0
- package/src/providers/shopify/transforms.ts +6 -1
- package/src/types/capabilities.types.ts +33 -0
- package/.turbo/turbo-test.log +0 -1
- package/.turbo/turbo-type-check.log +0 -0
- package/publish.log +0 -93
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Address,
|
|
3
|
+
AuthConfig,
|
|
4
|
+
AuthProvider,
|
|
5
|
+
AuthResponse,
|
|
6
|
+
CreateAddressData,
|
|
7
|
+
Customer,
|
|
8
|
+
LoginCredentials,
|
|
9
|
+
RegisterData,
|
|
10
|
+
UpdateCustomerData,
|
|
11
|
+
} from '@thorprovider/types';
|
|
12
|
+
import { createLogger } from '../../core/logger';
|
|
13
|
+
import { ProviderAPIError, extractErrorMessage } from '../../core/errors';
|
|
14
|
+
import { AuthStorage } from '../../lib/auth-storage';
|
|
15
|
+
import {
|
|
16
|
+
CUSTOMER_ACCESS_TOKEN_CREATE,
|
|
17
|
+
CUSTOMER_ACCESS_TOKEN_DELETE,
|
|
18
|
+
CUSTOMER_ADDRESS_CREATE,
|
|
19
|
+
CUSTOMER_ADDRESS_DELETE,
|
|
20
|
+
CUSTOMER_ADDRESS_UPDATE,
|
|
21
|
+
CUSTOMER_CREATE,
|
|
22
|
+
CUSTOMER_RECOVER,
|
|
23
|
+
CUSTOMER_RESET,
|
|
24
|
+
CUSTOMER_UPDATE,
|
|
25
|
+
} from './mutations';
|
|
26
|
+
import { GET_CUSTOMER } from './queries';
|
|
27
|
+
import { transformAddress, transformCustomer, transformCustomerAddresses } from './transforms';
|
|
28
|
+
|
|
29
|
+
type ShopifyGraphqlRequester = <T = any>(gql: string, variables?: Record<string, unknown>) => Promise<T>;
|
|
30
|
+
|
|
31
|
+
interface ShopifyCustomerToken {
|
|
32
|
+
accessToken: string;
|
|
33
|
+
expiresAt?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ShopifyAuthDependencies {
|
|
37
|
+
query: ShopifyGraphqlRequester;
|
|
38
|
+
authConfig: AuthConfig;
|
|
39
|
+
debug?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class ShopifyAuthProvider implements AuthProvider {
|
|
43
|
+
private readonly logger;
|
|
44
|
+
private readonly storage: AuthStorage;
|
|
45
|
+
|
|
46
|
+
constructor(private readonly deps: ShopifyAuthDependencies) {
|
|
47
|
+
this.logger = createLogger('ShopifyAuthProvider', deps.debug);
|
|
48
|
+
this.storage = new AuthStorage(deps.authConfig, 'thor:shopify:auth');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private async readToken(): Promise<ShopifyCustomerToken | null> {
|
|
52
|
+
const token = await this.storage.getJson<ShopifyCustomerToken>('customer-access-token');
|
|
53
|
+
if (!token?.accessToken) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (token.expiresAt && new Date(token.expiresAt).getTime() <= Date.now()) {
|
|
58
|
+
await this.storage.remove('customer-access-token');
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return token;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getStoredToken(): Promise<ShopifyCustomerToken | null> {
|
|
66
|
+
return this.readToken();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async writeToken(token: ShopifyCustomerToken | null): Promise<void> {
|
|
70
|
+
if (!token) {
|
|
71
|
+
await this.storage.remove('customer-access-token');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
await this.storage.setJson('customer-access-token', token);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async requireToken(): Promise<ShopifyCustomerToken> {
|
|
79
|
+
const token = await this.readToken();
|
|
80
|
+
if (!token) {
|
|
81
|
+
throw new ProviderAPIError('Shopify customer access token is required.', 'shopify', 401);
|
|
82
|
+
}
|
|
83
|
+
return token;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async fetchCurrentCustomer(): Promise<any | null> {
|
|
87
|
+
const token = await this.readToken();
|
|
88
|
+
if (!token) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const data = await this.deps.query<{ customer: any }>(GET_CUSTOMER, {
|
|
94
|
+
customerAccessToken: token.accessToken,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return data.customer ?? null;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
await this.writeToken(null);
|
|
100
|
+
this.logger.warn('Failed to fetch current Shopify customer', extractErrorMessage(error));
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async login(credentials: LoginCredentials): Promise<AuthResponse> {
|
|
106
|
+
try {
|
|
107
|
+
const data = await this.deps.query<{
|
|
108
|
+
customerAccessTokenCreate: {
|
|
109
|
+
customerAccessToken?: ShopifyCustomerToken;
|
|
110
|
+
customerUserErrors?: Array<{ message: string }>;
|
|
111
|
+
};
|
|
112
|
+
}>(CUSTOMER_ACCESS_TOKEN_CREATE, {
|
|
113
|
+
input: {
|
|
114
|
+
email: credentials.email,
|
|
115
|
+
password: credentials.password,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const result = data.customerAccessTokenCreate;
|
|
120
|
+
if (result.customerUserErrors?.length || !result.customerAccessToken?.accessToken) {
|
|
121
|
+
throw new ProviderAPIError(
|
|
122
|
+
result.customerUserErrors?.map((entry) => entry.message).join('; ') || 'Shopify login failed.',
|
|
123
|
+
'shopify',
|
|
124
|
+
400,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await this.writeToken(result.customerAccessToken);
|
|
129
|
+
const customer = await this.getCurrentCustomer();
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
token: result.customerAccessToken.accessToken,
|
|
133
|
+
customer: customer ?? undefined,
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw new ProviderAPIError(`Shopify login failed: ${extractErrorMessage(error)}`, 'shopify', 400);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async register(data: RegisterData): Promise<AuthResponse> {
|
|
141
|
+
try {
|
|
142
|
+
const createResult = await this.deps.query<{
|
|
143
|
+
customerCreate: {
|
|
144
|
+
customer?: any;
|
|
145
|
+
customerUserErrors?: Array<{ message: string }>;
|
|
146
|
+
};
|
|
147
|
+
}>(CUSTOMER_CREATE, {
|
|
148
|
+
input: {
|
|
149
|
+
firstName: data.first_name,
|
|
150
|
+
lastName: data.last_name,
|
|
151
|
+
email: data.email,
|
|
152
|
+
phone: data.phone,
|
|
153
|
+
password: data.password,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const result = createResult.customerCreate;
|
|
158
|
+
if (result.customerUserErrors?.length) {
|
|
159
|
+
throw new ProviderAPIError(
|
|
160
|
+
result.customerUserErrors.map((entry) => entry.message).join('; '),
|
|
161
|
+
'shopify',
|
|
162
|
+
400,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return this.login({ email: data.email, password: data.password });
|
|
167
|
+
} catch (error) {
|
|
168
|
+
throw new ProviderAPIError(`Shopify registration failed: ${extractErrorMessage(error)}`, 'shopify', 400);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async logout(): Promise<void> {
|
|
173
|
+
try {
|
|
174
|
+
const token = await this.readToken();
|
|
175
|
+
if (token?.accessToken) {
|
|
176
|
+
await this.deps.query(CUSTOMER_ACCESS_TOKEN_DELETE, {
|
|
177
|
+
customerAccessToken: token.accessToken,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
this.logger.warn('Shopify logout returned an error', extractErrorMessage(error));
|
|
182
|
+
} finally {
|
|
183
|
+
await this.writeToken(null);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async requestPasswordReset(email: string): Promise<void> {
|
|
188
|
+
try {
|
|
189
|
+
const data = await this.deps.query<{
|
|
190
|
+
customerRecover: {
|
|
191
|
+
customerUserErrors?: Array<{ message: string }>;
|
|
192
|
+
};
|
|
193
|
+
}>(CUSTOMER_RECOVER, { email });
|
|
194
|
+
|
|
195
|
+
if (data.customerRecover.customerUserErrors?.length) {
|
|
196
|
+
throw new ProviderAPIError(
|
|
197
|
+
data.customerRecover.customerUserErrors.map((entry) => entry.message).join('; '),
|
|
198
|
+
'shopify',
|
|
199
|
+
400,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
throw new ProviderAPIError(`Shopify password reset request failed: ${extractErrorMessage(error)}`, 'shopify', 400);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async resetPassword(token: string, password: string, metadata?: Record<string, unknown>): Promise<void> {
|
|
208
|
+
const customerId = typeof metadata?.customerId === 'string' ? metadata.customerId : null;
|
|
209
|
+
if (!customerId) {
|
|
210
|
+
throw new ProviderAPIError('Shopify password reset requires metadata.customerId.', 'shopify', 400);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const data = await this.deps.query<{
|
|
215
|
+
customerReset: {
|
|
216
|
+
customerAccessToken?: ShopifyCustomerToken;
|
|
217
|
+
customerUserErrors?: Array<{ message: string }>;
|
|
218
|
+
};
|
|
219
|
+
}>(CUSTOMER_RESET, {
|
|
220
|
+
id: customerId,
|
|
221
|
+
input: {
|
|
222
|
+
resetToken: token,
|
|
223
|
+
password,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (data.customerReset.customerUserErrors?.length) {
|
|
228
|
+
throw new ProviderAPIError(
|
|
229
|
+
data.customerReset.customerUserErrors.map((entry) => entry.message).join('; '),
|
|
230
|
+
'shopify',
|
|
231
|
+
400,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (data.customerReset.customerAccessToken?.accessToken) {
|
|
236
|
+
await this.writeToken(data.customerReset.customerAccessToken);
|
|
237
|
+
}
|
|
238
|
+
} catch (error) {
|
|
239
|
+
throw new ProviderAPIError(`Shopify password reset failed: ${extractErrorMessage(error)}`, 'shopify', 400);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async getCurrentCustomer(): Promise<Customer | null> {
|
|
244
|
+
const customer = await this.fetchCurrentCustomer();
|
|
245
|
+
return customer ? transformCustomer(customer) : null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async updateCustomer(data: UpdateCustomerData): Promise<Customer> {
|
|
249
|
+
const token = await this.requireToken();
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const payload: Record<string, unknown> = {};
|
|
253
|
+
if (data.first_name !== undefined) payload.firstName = data.first_name;
|
|
254
|
+
if (data.last_name !== undefined) payload.lastName = data.last_name;
|
|
255
|
+
if (data.email !== undefined) payload.email = data.email;
|
|
256
|
+
if (data.phone !== undefined && data.phone !== null) payload.phone = data.phone;
|
|
257
|
+
if (data.password !== undefined) payload.password = data.password;
|
|
258
|
+
|
|
259
|
+
const response = await this.deps.query<{
|
|
260
|
+
customerUpdate: {
|
|
261
|
+
customer?: any;
|
|
262
|
+
customerAccessToken?: ShopifyCustomerToken;
|
|
263
|
+
customerUserErrors?: Array<{ message: string }>;
|
|
264
|
+
};
|
|
265
|
+
}>(CUSTOMER_UPDATE, {
|
|
266
|
+
customerAccessToken: token.accessToken,
|
|
267
|
+
customer: payload,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const result = response.customerUpdate;
|
|
271
|
+
if (result.customerUserErrors?.length || !result.customer) {
|
|
272
|
+
throw new ProviderAPIError(
|
|
273
|
+
result.customerUserErrors?.map((entry) => entry.message).join('; ') || 'Shopify customer update failed.',
|
|
274
|
+
'shopify',
|
|
275
|
+
400,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (result.customerAccessToken?.accessToken) {
|
|
280
|
+
await this.writeToken(result.customerAccessToken);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return transformCustomer(result.customer);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
throw new ProviderAPIError(`Shopify update customer failed: ${extractErrorMessage(error)}`, 'shopify', 400);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async isAuthenticated(): Promise<boolean> {
|
|
290
|
+
return (await this.getCurrentCustomer()) !== null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
getAuthToken(): string | null {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async getAddresses(): Promise<Address[]> {
|
|
298
|
+
const customer = await this.fetchCurrentCustomer();
|
|
299
|
+
return customer ? transformCustomerAddresses(customer) : [];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private toMailingAddressInput(data: Partial<CreateAddressData>): Record<string, unknown> {
|
|
303
|
+
return {
|
|
304
|
+
firstName: data.firstName,
|
|
305
|
+
lastName: data.lastName,
|
|
306
|
+
company: data.company,
|
|
307
|
+
address1: data.address1,
|
|
308
|
+
address2: data.address2,
|
|
309
|
+
city: data.city,
|
|
310
|
+
province: data.province,
|
|
311
|
+
country: data.countryCode,
|
|
312
|
+
zip: data.postalCode,
|
|
313
|
+
phone: data.phone,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async createAddress(data: CreateAddressData): Promise<Address> {
|
|
318
|
+
const token = await this.requireToken();
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
const response = await this.deps.query<{
|
|
322
|
+
customerAddressCreate: {
|
|
323
|
+
customerAddress?: any;
|
|
324
|
+
customerUserErrors?: Array<{ message: string }>;
|
|
325
|
+
};
|
|
326
|
+
}>(CUSTOMER_ADDRESS_CREATE, {
|
|
327
|
+
customerAccessToken: token.accessToken,
|
|
328
|
+
address: this.toMailingAddressInput(data),
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
if (response.customerAddressCreate.customerUserErrors?.length || !response.customerAddressCreate.customerAddress) {
|
|
332
|
+
throw new ProviderAPIError(
|
|
333
|
+
response.customerAddressCreate.customerUserErrors?.map((entry) => entry.message).join('; ') || 'Shopify address create failed.',
|
|
334
|
+
'shopify',
|
|
335
|
+
400,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return transformAddress(response.customerAddressCreate.customerAddress);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
throw new ProviderAPIError(`Shopify create address failed: ${extractErrorMessage(error)}`, 'shopify', 400);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async updateAddress(addressId: string, data: Partial<CreateAddressData>): Promise<Address> {
|
|
346
|
+
const token = await this.requireToken();
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
const response = await this.deps.query<{
|
|
350
|
+
customerAddressUpdate: {
|
|
351
|
+
customerAddress?: any;
|
|
352
|
+
customerUserErrors?: Array<{ message: string }>;
|
|
353
|
+
};
|
|
354
|
+
}>(CUSTOMER_ADDRESS_UPDATE, {
|
|
355
|
+
customerAccessToken: token.accessToken,
|
|
356
|
+
id: addressId,
|
|
357
|
+
address: this.toMailingAddressInput(data),
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
if (response.customerAddressUpdate.customerUserErrors?.length || !response.customerAddressUpdate.customerAddress) {
|
|
361
|
+
throw new ProviderAPIError(
|
|
362
|
+
response.customerAddressUpdate.customerUserErrors?.map((entry) => entry.message).join('; ') || 'Shopify address update failed.',
|
|
363
|
+
'shopify',
|
|
364
|
+
400,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return transformAddress(response.customerAddressUpdate.customerAddress);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
throw new ProviderAPIError(`Shopify update address failed: ${extractErrorMessage(error)}`, 'shopify', 400);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async deleteAddress(addressId: string): Promise<void> {
|
|
375
|
+
const token = await this.requireToken();
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
const response = await this.deps.query<{
|
|
379
|
+
customerAddressDelete: {
|
|
380
|
+
customerUserErrors?: Array<{ message: string }>;
|
|
381
|
+
};
|
|
382
|
+
}>(CUSTOMER_ADDRESS_DELETE, {
|
|
383
|
+
customerAccessToken: token.accessToken,
|
|
384
|
+
id: addressId,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
if (response.customerAddressDelete.customerUserErrors?.length) {
|
|
388
|
+
throw new ProviderAPIError(
|
|
389
|
+
response.customerAddressDelete.customerUserErrors.map((entry) => entry.message).join('; '),
|
|
390
|
+
'shopify',
|
|
391
|
+
400,
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
} catch (error) {
|
|
395
|
+
throw new ProviderAPIError(`Shopify delete address failed: ${extractErrorMessage(error)}`, 'shopify', 400);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async deleteCustomerAccount(): Promise<void> {
|
|
400
|
+
throw new ProviderAPIError('Shopify customer self-service deletion requires Admin API and is not supported.', 'shopify', 501);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
@@ -100,3 +100,148 @@ export const CUSTOMER_ACCESS_TOKEN_CREATE = `
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
`;
|
|
103
|
+
|
|
104
|
+
export const CUSTOMER_ACCESS_TOKEN_DELETE = `
|
|
105
|
+
mutation CustomerAccessTokenDelete($customerAccessToken: String!) {
|
|
106
|
+
customerAccessTokenDelete(customerAccessToken: $customerAccessToken) {
|
|
107
|
+
deletedAccessToken
|
|
108
|
+
deletedCustomerAccessTokenId
|
|
109
|
+
userErrors {
|
|
110
|
+
field
|
|
111
|
+
message
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
export const CUSTOMER_CREATE = `
|
|
118
|
+
mutation CustomerCreate($input: CustomerCreateInput!) {
|
|
119
|
+
customerCreate(input: $input) {
|
|
120
|
+
customer {
|
|
121
|
+
id
|
|
122
|
+
email
|
|
123
|
+
firstName
|
|
124
|
+
lastName
|
|
125
|
+
}
|
|
126
|
+
customerUserErrors {
|
|
127
|
+
field
|
|
128
|
+
message
|
|
129
|
+
code
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
export const CUSTOMER_UPDATE = `
|
|
136
|
+
mutation CustomerUpdate($customerAccessToken: String!, $customer: CustomerUpdateInput!) {
|
|
137
|
+
customerUpdate(customerAccessToken: $customerAccessToken, customer: $customer) {
|
|
138
|
+
customer {
|
|
139
|
+
id
|
|
140
|
+
email
|
|
141
|
+
firstName
|
|
142
|
+
lastName
|
|
143
|
+
phone
|
|
144
|
+
}
|
|
145
|
+
customerAccessToken {
|
|
146
|
+
accessToken
|
|
147
|
+
expiresAt
|
|
148
|
+
}
|
|
149
|
+
customerUserErrors {
|
|
150
|
+
field
|
|
151
|
+
message
|
|
152
|
+
code
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
export const CUSTOMER_RECOVER = `
|
|
159
|
+
mutation CustomerRecover($email: String!) {
|
|
160
|
+
customerRecover(email: $email) {
|
|
161
|
+
customerUserErrors {
|
|
162
|
+
field
|
|
163
|
+
message
|
|
164
|
+
code
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
export const CUSTOMER_RESET = `
|
|
171
|
+
mutation CustomerReset($id: ID!, $input: CustomerResetInput!) {
|
|
172
|
+
customerReset(id: $id, input: $input) {
|
|
173
|
+
customerAccessToken {
|
|
174
|
+
accessToken
|
|
175
|
+
expiresAt
|
|
176
|
+
}
|
|
177
|
+
customerUserErrors {
|
|
178
|
+
field
|
|
179
|
+
message
|
|
180
|
+
code
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
|
|
186
|
+
export const CUSTOMER_ADDRESS_CREATE = `
|
|
187
|
+
mutation CustomerAddressCreate($customerAccessToken: String!, $address: MailingAddressInput!) {
|
|
188
|
+
customerAddressCreate(customerAccessToken: $customerAccessToken, address: $address) {
|
|
189
|
+
customerAddress {
|
|
190
|
+
id
|
|
191
|
+
firstName
|
|
192
|
+
lastName
|
|
193
|
+
address1
|
|
194
|
+
address2
|
|
195
|
+
city
|
|
196
|
+
province
|
|
197
|
+
country
|
|
198
|
+
zip
|
|
199
|
+
phone
|
|
200
|
+
company
|
|
201
|
+
}
|
|
202
|
+
customerUserErrors {
|
|
203
|
+
field
|
|
204
|
+
message
|
|
205
|
+
code
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
|
|
211
|
+
export const CUSTOMER_ADDRESS_UPDATE = `
|
|
212
|
+
mutation CustomerAddressUpdate($customerAccessToken: String!, $id: ID!, $address: MailingAddressInput!) {
|
|
213
|
+
customerAddressUpdate(customerAccessToken: $customerAccessToken, id: $id, address: $address) {
|
|
214
|
+
customerAddress {
|
|
215
|
+
id
|
|
216
|
+
firstName
|
|
217
|
+
lastName
|
|
218
|
+
address1
|
|
219
|
+
address2
|
|
220
|
+
city
|
|
221
|
+
province
|
|
222
|
+
country
|
|
223
|
+
zip
|
|
224
|
+
phone
|
|
225
|
+
company
|
|
226
|
+
}
|
|
227
|
+
customerUserErrors {
|
|
228
|
+
field
|
|
229
|
+
message
|
|
230
|
+
code
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
export const CUSTOMER_ADDRESS_DELETE = `
|
|
237
|
+
mutation CustomerAddressDelete($customerAccessToken: String!, $id: ID!) {
|
|
238
|
+
customerAddressDelete(customerAccessToken: $customerAccessToken, id: $id) {
|
|
239
|
+
deletedCustomerAddressId
|
|
240
|
+
customerUserErrors {
|
|
241
|
+
field
|
|
242
|
+
message
|
|
243
|
+
code
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
@@ -171,7 +171,7 @@ export function transformCart(raw: any): Cart {
|
|
|
171
171
|
// Customer Transforms
|
|
172
172
|
// ============================================
|
|
173
173
|
|
|
174
|
-
function transformAddress(raw: any): Address {
|
|
174
|
+
export function transformAddress(raw: any): Address {
|
|
175
175
|
return {
|
|
176
176
|
id: raw.id ?? '',
|
|
177
177
|
firstName: raw.firstName ?? '',
|
|
@@ -198,6 +198,11 @@ export function transformCustomer(raw: any): Customer {
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
export function transformCustomerAddresses(raw: any): Address[] {
|
|
202
|
+
const edges = raw.addresses?.edges ?? [];
|
|
203
|
+
return edges.map((edge: any) => transformAddress(edge.node));
|
|
204
|
+
}
|
|
205
|
+
|
|
201
206
|
// ============================================
|
|
202
207
|
// Order Transforms
|
|
203
208
|
// ============================================
|
|
@@ -16,6 +16,39 @@ import type { BackendCapabilities } from '@thorprovider/types';
|
|
|
16
16
|
|
|
17
17
|
export type { BackendCapabilities };
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Mock backend capabilities
|
|
21
|
+
*
|
|
22
|
+
* Fixture-backed provider used when a storefront has not yet been connected
|
|
23
|
+
* to a real backend. Supports the minimum commerce flows needed for layout,
|
|
24
|
+
* preview, navigation, region switching, and cart prototyping.
|
|
25
|
+
*/
|
|
26
|
+
export const MOCK_CAPABILITIES: Readonly<BackendCapabilities> = {
|
|
27
|
+
updateProfile: false,
|
|
28
|
+
updateEmail: false,
|
|
29
|
+
changePasswordDirect: false,
|
|
30
|
+
changePasswordViaEmail: false,
|
|
31
|
+
deleteAccount: false,
|
|
32
|
+
uploadAvatar: false,
|
|
33
|
+
avatarViaMetadata: false,
|
|
34
|
+
customAttributes: false,
|
|
35
|
+
fetchOrders: true,
|
|
36
|
+
cancelOrder: true,
|
|
37
|
+
manageAddresses: false,
|
|
38
|
+
defaultAddress: false,
|
|
39
|
+
applyDiscounts: true,
|
|
40
|
+
shippingCalculation: true,
|
|
41
|
+
guestCheckout: true,
|
|
42
|
+
savedPaymentMethods: false,
|
|
43
|
+
listPaymentProviders: true,
|
|
44
|
+
regionSpecificPayments: true,
|
|
45
|
+
wishlist: false,
|
|
46
|
+
advancedSearch: false,
|
|
47
|
+
facetedFilters: false,
|
|
48
|
+
productReviews: false,
|
|
49
|
+
loyaltyProgram: false,
|
|
50
|
+
};
|
|
51
|
+
|
|
19
52
|
/**
|
|
20
53
|
* Medusa backend capabilities
|
|
21
54
|
*
|
package/.turbo/turbo-test.log
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
command not found: jest
|
|
File without changes
|
package/publish.log
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
npm warn Unknown project config "network-concurrency". This will stop working in the next major version of npm.
|
|
2
|
-
npm warn publish npm auto-corrected some errors in your package.json when publishing. Please run "npm pkg fix" to address these errors.
|
|
3
|
-
npm warn publish errors corrected:
|
|
4
|
-
npm warn publish "repository.url" was normalized to "git+https://github.com/thorprovider/thor-commerce.git"
|
|
5
|
-
npm notice
|
|
6
|
-
npm notice 📦 @thor/adapters@3.0.1
|
|
7
|
-
npm notice Tarball Contents
|
|
8
|
-
npm notice 629B AGENTS.md
|
|
9
|
-
npm notice 9.1kB CHANGELOG.md
|
|
10
|
-
npm notice 19.2kB README.md
|
|
11
|
-
npm notice 73.6kB dist/index.d.mts
|
|
12
|
-
npm notice 73.6kB dist/index.d.ts
|
|
13
|
-
npm notice 191.6kB dist/index.js
|
|
14
|
-
npm notice 377.7kB dist/index.js.map
|
|
15
|
-
npm notice 187.4kB dist/index.mjs
|
|
16
|
-
npm notice 377.4kB dist/index.mjs.map
|
|
17
|
-
npm notice 4.7kB docs/authentication.md
|
|
18
|
-
npm notice 4.3kB docs/capabilities-system.md
|
|
19
|
-
npm notice 9.9kB docs/category-customization.md
|
|
20
|
-
npm notice 11.0kB docs/fetch-reliability-solution.md
|
|
21
|
-
npm notice 4.4kB docs/getting-started.md
|
|
22
|
-
npm notice 7.8kB docs/multi-tenant-channel-scoping.md
|
|
23
|
-
npm notice 4.5kB docs/profile-management.md
|
|
24
|
-
npm notice 3.7kB docs/providers-overview.md
|
|
25
|
-
npm notice 2.1kB docs/RETRY_SOLUTION_SUMMARY.md
|
|
26
|
-
npm notice 5.2kB docs/services-overview.md
|
|
27
|
-
npm notice 4.0kB docs/update-documentation.md
|
|
28
|
-
npm notice 1.2kB package.json
|
|
29
|
-
npm notice 6.3kB quick-reference.md
|
|
30
|
-
npm notice 2.5kB src/admin/AdminAuthService.ts
|
|
31
|
-
npm notice 2.4kB src/admin/AdminSessionService.ts
|
|
32
|
-
npm notice 414B src/admin/index.ts
|
|
33
|
-
npm notice 4.2kB src/admin/MetricsService.ts
|
|
34
|
-
npm notice 2.5kB src/backend/BackendMetadataAdapter.ts
|
|
35
|
-
npm notice 3.5kB src/backend/factory.ts
|
|
36
|
-
npm notice 312B src/backend/index.ts
|
|
37
|
-
npm notice 3.9kB src/backend/medusa/MedusaMetadataAdapter.ts
|
|
38
|
-
npm notice 6.3kB src/core/errors.js
|
|
39
|
-
npm notice 3.5kB src/core/errors.ts
|
|
40
|
-
npm notice 11.3kB src/core/fetch-utils.js
|
|
41
|
-
npm notice 6.0kB src/core/fetch-utils.ts
|
|
42
|
-
npm notice 141B src/core/index.ts
|
|
43
|
-
npm notice 2.3kB src/core/logger.js
|
|
44
|
-
npm notice 1.1kB src/core/logger.ts
|
|
45
|
-
npm notice 2.0kB src/factory/index.ts
|
|
46
|
-
npm notice 4.5kB src/index.ts
|
|
47
|
-
npm notice 2.7kB src/lib/money-transform.ts
|
|
48
|
-
npm notice 6.2kB src/metadata/channel-scope-validation.js
|
|
49
|
-
npm notice 5.1kB src/metadata/channel-scope-validation.ts
|
|
50
|
-
npm notice 7.6kB src/metadata/channel-scope.js
|
|
51
|
-
npm notice 8.2kB src/metadata/channel-scope.ts
|
|
52
|
-
npm notice 3.3kB src/metadata/index.js
|
|
53
|
-
npm notice 810B src/metadata/index.ts
|
|
54
|
-
npm notice 148B src/providers/index.ts
|
|
55
|
-
npm notice 30.0kB src/providers/medusa/auth.js
|
|
56
|
-
npm notice 15.1kB src/providers/medusa/auth.ts
|
|
57
|
-
npm notice 6.2kB src/providers/medusa/fulfillment-planner.ts
|
|
58
|
-
npm notice 151.8kB src/providers/medusa/index.js
|
|
59
|
-
npm notice 147.6kB src/providers/medusa/index.ts
|
|
60
|
-
npm notice 184B src/providers/medusa/medusa.ts
|
|
61
|
-
npm notice 11.0kB src/providers/medusa/payment.js
|
|
62
|
-
npm notice 7.2kB src/providers/medusa/payment.ts
|
|
63
|
-
npm notice 25.8kB src/providers/medusa/transforms.js
|
|
64
|
-
npm notice 19.8kB src/providers/medusa/transforms.ts
|
|
65
|
-
npm notice 5.5kB src/resilience/circuit-breaker.ts
|
|
66
|
-
npm notice 238B src/resilience/index.ts
|
|
67
|
-
npm notice 7.6kB src/resilience/README.md
|
|
68
|
-
npm notice 4.8kB src/types/capabilities.types.js
|
|
69
|
-
npm notice 4.7kB src/types/capabilities.types.ts
|
|
70
|
-
npm notice 513B src/types/index.ts
|
|
71
|
-
npm notice 350B src/types/provider.js
|
|
72
|
-
npm notice 365B src/types/provider.ts
|
|
73
|
-
npm notice 6.5kB src/types/ui-responses.ts
|
|
74
|
-
npm notice 1.1kB src/utils/city-coordinates.ts
|
|
75
|
-
npm notice 1.3kB src/utils/geolocation.ts
|
|
76
|
-
npm notice 5.6kB src/utils/payment-utils.js
|
|
77
|
-
npm notice 6.5kB src/utils/payment-utils.ts
|
|
78
|
-
npm notice 357B tsconfig.json
|
|
79
|
-
npm notice 370B tsup.config.ts
|
|
80
|
-
npm notice Tarball Details
|
|
81
|
-
npm notice name: @thor/adapters
|
|
82
|
-
npm notice version: 3.0.1
|
|
83
|
-
npm notice filename: thor-adapters-3.0.1.tgz
|
|
84
|
-
npm notice package size: 413.9 kB
|
|
85
|
-
npm notice unpacked size: 1.9 MB
|
|
86
|
-
npm notice shasum: 696c58c4b5629ad1ec09336238ea157aad015e3d
|
|
87
|
-
npm notice integrity: sha512-tIweHlz73prg8[...]3KYkFTyNlUX4w==
|
|
88
|
-
npm notice total files: 72
|
|
89
|
-
npm notice
|
|
90
|
-
npm notice Publishing to https://npm.pkg.github.com with tag latest and restricted access
|
|
91
|
-
npm error code E401
|
|
92
|
-
npm error 401 Unauthorized - PUT https://npm.pkg.github.com/@thor%2fadapters - unauthenticated: User cannot be authenticated with the token provided.
|
|
93
|
-
npm error A complete log of this run can be found in: /home/nelson/.npm/_logs/2026-04-16T15_45_37_415Z-debug-0.log
|